annotate gcc/config/rs6000/rs6000-string.c @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 /* Subroutines used to expand string and block move, clear,
kono
parents:
diff changeset
2 compare and other operations for PowerPC.
kono
parents:
diff changeset
3 Copyright (C) 1991-2017 Free Software Foundation, Inc.
kono
parents:
diff changeset
4
kono
parents:
diff changeset
5 This file is part of GCC.
kono
parents:
diff changeset
6
kono
parents:
diff changeset
7 GCC is free software; you can redistribute it and/or modify it
kono
parents:
diff changeset
8 under the terms of the GNU General Public License as published
kono
parents:
diff changeset
9 by the Free Software Foundation; either version 3, or (at your
kono
parents:
diff changeset
10 option) any later version.
kono
parents:
diff changeset
11
kono
parents:
diff changeset
12 GCC is distributed in the hope that it will be useful, but WITHOUT
kono
parents:
diff changeset
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
kono
parents:
diff changeset
14 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
kono
parents:
diff changeset
15 License for more details.
kono
parents:
diff changeset
16
kono
parents:
diff changeset
17 You should have received a copy of the GNU General Public License
kono
parents:
diff changeset
18 along with GCC; see the file COPYING3. If not see
kono
parents:
diff changeset
19 <http://www.gnu.org/licenses/>. */
kono
parents:
diff changeset
20
kono
parents:
diff changeset
21 #include "config.h"
kono
parents:
diff changeset
22 #include "system.h"
kono
parents:
diff changeset
23 #include "coretypes.h"
kono
parents:
diff changeset
24 #include "backend.h"
kono
parents:
diff changeset
25 #include "rtl.h"
kono
parents:
diff changeset
26 #include "tree.h"
kono
parents:
diff changeset
27 #include "memmodel.h"
kono
parents:
diff changeset
28 #include "tm_p.h"
kono
parents:
diff changeset
29 #include "ira.h"
kono
parents:
diff changeset
30 #include "print-tree.h"
kono
parents:
diff changeset
31 #include "varasm.h"
kono
parents:
diff changeset
32 #include "explow.h"
kono
parents:
diff changeset
33 #include "expr.h"
kono
parents:
diff changeset
34 #include "output.h"
kono
parents:
diff changeset
35 #include "target.h"
kono
parents:
diff changeset
36
kono
parents:
diff changeset
37 /* Expand a block clear operation, and return 1 if successful. Return 0
kono
parents:
diff changeset
38 if we should let the compiler generate normal code.
kono
parents:
diff changeset
39
kono
parents:
diff changeset
40 operands[0] is the destination
kono
parents:
diff changeset
41 operands[1] is the length
kono
parents:
diff changeset
42 operands[3] is the alignment */
kono
parents:
diff changeset
43
kono
parents:
diff changeset
44 int
kono
parents:
diff changeset
45 expand_block_clear (rtx operands[])
kono
parents:
diff changeset
46 {
kono
parents:
diff changeset
47 rtx orig_dest = operands[0];
kono
parents:
diff changeset
48 rtx bytes_rtx = operands[1];
kono
parents:
diff changeset
49 rtx align_rtx = operands[3];
kono
parents:
diff changeset
50 bool constp = (GET_CODE (bytes_rtx) == CONST_INT);
kono
parents:
diff changeset
51 HOST_WIDE_INT align;
kono
parents:
diff changeset
52 HOST_WIDE_INT bytes;
kono
parents:
diff changeset
53 int offset;
kono
parents:
diff changeset
54 int clear_bytes;
kono
parents:
diff changeset
55 int clear_step;
kono
parents:
diff changeset
56
kono
parents:
diff changeset
57 /* If this is not a fixed size move, just call memcpy */
kono
parents:
diff changeset
58 if (! constp)
kono
parents:
diff changeset
59 return 0;
kono
parents:
diff changeset
60
kono
parents:
diff changeset
61 /* This must be a fixed size alignment */
kono
parents:
diff changeset
62 gcc_assert (GET_CODE (align_rtx) == CONST_INT);
kono
parents:
diff changeset
63 align = INTVAL (align_rtx) * BITS_PER_UNIT;
kono
parents:
diff changeset
64
kono
parents:
diff changeset
65 /* Anything to clear? */
kono
parents:
diff changeset
66 bytes = INTVAL (bytes_rtx);
kono
parents:
diff changeset
67 if (bytes <= 0)
kono
parents:
diff changeset
68 return 1;
kono
parents:
diff changeset
69
kono
parents:
diff changeset
70 /* Use the builtin memset after a point, to avoid huge code bloat.
kono
parents:
diff changeset
71 When optimize_size, avoid any significant code bloat; calling
kono
parents:
diff changeset
72 memset is about 4 instructions, so allow for one instruction to
kono
parents:
diff changeset
73 load zero and three to do clearing. */
kono
parents:
diff changeset
74 if (TARGET_ALTIVEC && align >= 128)
kono
parents:
diff changeset
75 clear_step = 16;
kono
parents:
diff changeset
76 else if (TARGET_POWERPC64 && (align >= 64 || !STRICT_ALIGNMENT))
kono
parents:
diff changeset
77 clear_step = 8;
kono
parents:
diff changeset
78 else
kono
parents:
diff changeset
79 clear_step = 4;
kono
parents:
diff changeset
80
kono
parents:
diff changeset
81 if (optimize_size && bytes > 3 * clear_step)
kono
parents:
diff changeset
82 return 0;
kono
parents:
diff changeset
83 if (! optimize_size && bytes > 8 * clear_step)
kono
parents:
diff changeset
84 return 0;
kono
parents:
diff changeset
85
kono
parents:
diff changeset
86 for (offset = 0; bytes > 0; offset += clear_bytes, bytes -= clear_bytes)
kono
parents:
diff changeset
87 {
kono
parents:
diff changeset
88 machine_mode mode = BLKmode;
kono
parents:
diff changeset
89 rtx dest;
kono
parents:
diff changeset
90
kono
parents:
diff changeset
91 if (bytes >= 16 && TARGET_ALTIVEC && align >= 128)
kono
parents:
diff changeset
92 {
kono
parents:
diff changeset
93 clear_bytes = 16;
kono
parents:
diff changeset
94 mode = V4SImode;
kono
parents:
diff changeset
95 }
kono
parents:
diff changeset
96 else if (bytes >= 8 && TARGET_POWERPC64
kono
parents:
diff changeset
97 && (align >= 64 || !STRICT_ALIGNMENT))
kono
parents:
diff changeset
98 {
kono
parents:
diff changeset
99 clear_bytes = 8;
kono
parents:
diff changeset
100 mode = DImode;
kono
parents:
diff changeset
101 if (offset == 0 && align < 64)
kono
parents:
diff changeset
102 {
kono
parents:
diff changeset
103 rtx addr;
kono
parents:
diff changeset
104
kono
parents:
diff changeset
105 /* If the address form is reg+offset with offset not a
kono
parents:
diff changeset
106 multiple of four, reload into reg indirect form here
kono
parents:
diff changeset
107 rather than waiting for reload. This way we get one
kono
parents:
diff changeset
108 reload, not one per store. */
kono
parents:
diff changeset
109 addr = XEXP (orig_dest, 0);
kono
parents:
diff changeset
110 if ((GET_CODE (addr) == PLUS || GET_CODE (addr) == LO_SUM)
kono
parents:
diff changeset
111 && GET_CODE (XEXP (addr, 1)) == CONST_INT
kono
parents:
diff changeset
112 && (INTVAL (XEXP (addr, 1)) & 3) != 0)
kono
parents:
diff changeset
113 {
kono
parents:
diff changeset
114 addr = copy_addr_to_reg (addr);
kono
parents:
diff changeset
115 orig_dest = replace_equiv_address (orig_dest, addr);
kono
parents:
diff changeset
116 }
kono
parents:
diff changeset
117 }
kono
parents:
diff changeset
118 }
kono
parents:
diff changeset
119 else if (bytes >= 4 && (align >= 32 || !STRICT_ALIGNMENT))
kono
parents:
diff changeset
120 { /* move 4 bytes */
kono
parents:
diff changeset
121 clear_bytes = 4;
kono
parents:
diff changeset
122 mode = SImode;
kono
parents:
diff changeset
123 }
kono
parents:
diff changeset
124 else if (bytes >= 2 && (align >= 16 || !STRICT_ALIGNMENT))
kono
parents:
diff changeset
125 { /* move 2 bytes */
kono
parents:
diff changeset
126 clear_bytes = 2;
kono
parents:
diff changeset
127 mode = HImode;
kono
parents:
diff changeset
128 }
kono
parents:
diff changeset
129 else /* move 1 byte at a time */
kono
parents:
diff changeset
130 {
kono
parents:
diff changeset
131 clear_bytes = 1;
kono
parents:
diff changeset
132 mode = QImode;
kono
parents:
diff changeset
133 }
kono
parents:
diff changeset
134
kono
parents:
diff changeset
135 dest = adjust_address (orig_dest, mode, offset);
kono
parents:
diff changeset
136
kono
parents:
diff changeset
137 emit_move_insn (dest, CONST0_RTX (mode));
kono
parents:
diff changeset
138 }
kono
parents:
diff changeset
139
kono
parents:
diff changeset
140 return 1;
kono
parents:
diff changeset
141 }
kono
parents:
diff changeset
142
kono
parents:
diff changeset
143 /* Figure out the correct instructions to generate to load data for
kono
parents:
diff changeset
144 block compare. MODE is used for the read from memory, and
kono
parents:
diff changeset
145 data is zero extended if REG is wider than MODE. If LE code
kono
parents:
diff changeset
146 is being generated, bswap loads are used.
kono
parents:
diff changeset
147
kono
parents:
diff changeset
148 REG is the destination register to move the data into.
kono
parents:
diff changeset
149 MEM is the memory block being read.
kono
parents:
diff changeset
150 MODE is the mode of memory to use for the read. */
kono
parents:
diff changeset
151 static void
kono
parents:
diff changeset
152 do_load_for_compare (rtx reg, rtx mem, machine_mode mode)
kono
parents:
diff changeset
153 {
kono
parents:
diff changeset
154 switch (GET_MODE (reg))
kono
parents:
diff changeset
155 {
kono
parents:
diff changeset
156 case E_DImode:
kono
parents:
diff changeset
157 switch (mode)
kono
parents:
diff changeset
158 {
kono
parents:
diff changeset
159 case E_QImode:
kono
parents:
diff changeset
160 emit_insn (gen_zero_extendqidi2 (reg, mem));
kono
parents:
diff changeset
161 break;
kono
parents:
diff changeset
162 case E_HImode:
kono
parents:
diff changeset
163 {
kono
parents:
diff changeset
164 rtx src = mem;
kono
parents:
diff changeset
165 if (!BYTES_BIG_ENDIAN)
kono
parents:
diff changeset
166 {
kono
parents:
diff changeset
167 src = gen_reg_rtx (HImode);
kono
parents:
diff changeset
168 emit_insn (gen_bswaphi2 (src, mem));
kono
parents:
diff changeset
169 }
kono
parents:
diff changeset
170 emit_insn (gen_zero_extendhidi2 (reg, src));
kono
parents:
diff changeset
171 break;
kono
parents:
diff changeset
172 }
kono
parents:
diff changeset
173 case E_SImode:
kono
parents:
diff changeset
174 {
kono
parents:
diff changeset
175 rtx src = mem;
kono
parents:
diff changeset
176 if (!BYTES_BIG_ENDIAN)
kono
parents:
diff changeset
177 {
kono
parents:
diff changeset
178 src = gen_reg_rtx (SImode);
kono
parents:
diff changeset
179 emit_insn (gen_bswapsi2 (src, mem));
kono
parents:
diff changeset
180 }
kono
parents:
diff changeset
181 emit_insn (gen_zero_extendsidi2 (reg, src));
kono
parents:
diff changeset
182 }
kono
parents:
diff changeset
183 break;
kono
parents:
diff changeset
184 case E_DImode:
kono
parents:
diff changeset
185 if (!BYTES_BIG_ENDIAN)
kono
parents:
diff changeset
186 emit_insn (gen_bswapdi2 (reg, mem));
kono
parents:
diff changeset
187 else
kono
parents:
diff changeset
188 emit_insn (gen_movdi (reg, mem));
kono
parents:
diff changeset
189 break;
kono
parents:
diff changeset
190 default:
kono
parents:
diff changeset
191 gcc_unreachable ();
kono
parents:
diff changeset
192 }
kono
parents:
diff changeset
193 break;
kono
parents:
diff changeset
194
kono
parents:
diff changeset
195 case E_SImode:
kono
parents:
diff changeset
196 switch (mode)
kono
parents:
diff changeset
197 {
kono
parents:
diff changeset
198 case E_QImode:
kono
parents:
diff changeset
199 emit_insn (gen_zero_extendqisi2 (reg, mem));
kono
parents:
diff changeset
200 break;
kono
parents:
diff changeset
201 case E_HImode:
kono
parents:
diff changeset
202 {
kono
parents:
diff changeset
203 rtx src = mem;
kono
parents:
diff changeset
204 if (!BYTES_BIG_ENDIAN)
kono
parents:
diff changeset
205 {
kono
parents:
diff changeset
206 src = gen_reg_rtx (HImode);
kono
parents:
diff changeset
207 emit_insn (gen_bswaphi2 (src, mem));
kono
parents:
diff changeset
208 }
kono
parents:
diff changeset
209 emit_insn (gen_zero_extendhisi2 (reg, src));
kono
parents:
diff changeset
210 break;
kono
parents:
diff changeset
211 }
kono
parents:
diff changeset
212 case E_SImode:
kono
parents:
diff changeset
213 if (!BYTES_BIG_ENDIAN)
kono
parents:
diff changeset
214 emit_insn (gen_bswapsi2 (reg, mem));
kono
parents:
diff changeset
215 else
kono
parents:
diff changeset
216 emit_insn (gen_movsi (reg, mem));
kono
parents:
diff changeset
217 break;
kono
parents:
diff changeset
218 case E_DImode:
kono
parents:
diff changeset
219 /* DImode is larger than the destination reg so is not expected. */
kono
parents:
diff changeset
220 gcc_unreachable ();
kono
parents:
diff changeset
221 break;
kono
parents:
diff changeset
222 default:
kono
parents:
diff changeset
223 gcc_unreachable ();
kono
parents:
diff changeset
224 }
kono
parents:
diff changeset
225 break;
kono
parents:
diff changeset
226 default:
kono
parents:
diff changeset
227 gcc_unreachable ();
kono
parents:
diff changeset
228 break;
kono
parents:
diff changeset
229 }
kono
parents:
diff changeset
230 }
kono
parents:
diff changeset
231
kono
parents:
diff changeset
232 /* Select the mode to be used for reading the next chunk of bytes
kono
parents:
diff changeset
233 in the compare.
kono
parents:
diff changeset
234
kono
parents:
diff changeset
235 OFFSET is the current read offset from the beginning of the block.
kono
parents:
diff changeset
236 BYTES is the number of bytes remaining to be read.
kono
parents:
diff changeset
237 ALIGN is the minimum alignment of the memory blocks being compared in bytes.
kono
parents:
diff changeset
238 WORD_MODE_OK indicates using WORD_MODE is allowed, else SImode is
kono
parents:
diff changeset
239 the largest allowable mode. */
kono
parents:
diff changeset
240 static machine_mode
kono
parents:
diff changeset
241 select_block_compare_mode (unsigned HOST_WIDE_INT offset,
kono
parents:
diff changeset
242 unsigned HOST_WIDE_INT bytes,
kono
parents:
diff changeset
243 unsigned HOST_WIDE_INT align, bool word_mode_ok)
kono
parents:
diff changeset
244 {
kono
parents:
diff changeset
245 /* First see if we can do a whole load unit
kono
parents:
diff changeset
246 as that will be more efficient than a larger load + shift. */
kono
parents:
diff changeset
247
kono
parents:
diff changeset
248 /* If big, use biggest chunk.
kono
parents:
diff changeset
249 If exactly chunk size, use that size.
kono
parents:
diff changeset
250 If remainder can be done in one piece with shifting, do that.
kono
parents:
diff changeset
251 Do largest chunk possible without violating alignment rules. */
kono
parents:
diff changeset
252
kono
parents:
diff changeset
253 /* The most we can read without potential page crossing. */
kono
parents:
diff changeset
254 unsigned HOST_WIDE_INT maxread = ROUND_UP (bytes, align);
kono
parents:
diff changeset
255
kono
parents:
diff changeset
256 if (word_mode_ok && bytes >= UNITS_PER_WORD)
kono
parents:
diff changeset
257 return word_mode;
kono
parents:
diff changeset
258 else if (bytes == GET_MODE_SIZE (SImode))
kono
parents:
diff changeset
259 return SImode;
kono
parents:
diff changeset
260 else if (bytes == GET_MODE_SIZE (HImode))
kono
parents:
diff changeset
261 return HImode;
kono
parents:
diff changeset
262 else if (bytes == GET_MODE_SIZE (QImode))
kono
parents:
diff changeset
263 return QImode;
kono
parents:
diff changeset
264 else if (bytes < GET_MODE_SIZE (SImode)
kono
parents:
diff changeset
265 && offset >= GET_MODE_SIZE (SImode) - bytes)
kono
parents:
diff changeset
266 /* This matches the case were we have SImode and 3 bytes
kono
parents:
diff changeset
267 and offset >= 1 and permits us to move back one and overlap
kono
parents:
diff changeset
268 with the previous read, thus avoiding having to shift
kono
parents:
diff changeset
269 unwanted bytes off of the input. */
kono
parents:
diff changeset
270 return SImode;
kono
parents:
diff changeset
271 else if (word_mode_ok && bytes < UNITS_PER_WORD
kono
parents:
diff changeset
272 && offset >= UNITS_PER_WORD-bytes)
kono
parents:
diff changeset
273 /* Similarly, if we can use DImode it will get matched here and
kono
parents:
diff changeset
274 can do an overlapping read that ends at the end of the block. */
kono
parents:
diff changeset
275 return word_mode;
kono
parents:
diff changeset
276 else if (word_mode_ok && maxread >= UNITS_PER_WORD)
kono
parents:
diff changeset
277 /* It is safe to do all remaining in one load of largest size,
kono
parents:
diff changeset
278 possibly with a shift to get rid of unwanted bytes. */
kono
parents:
diff changeset
279 return word_mode;
kono
parents:
diff changeset
280 else if (maxread >= GET_MODE_SIZE (SImode))
kono
parents:
diff changeset
281 /* It is safe to do all remaining in one SImode load,
kono
parents:
diff changeset
282 possibly with a shift to get rid of unwanted bytes. */
kono
parents:
diff changeset
283 return SImode;
kono
parents:
diff changeset
284 else if (bytes > GET_MODE_SIZE (SImode))
kono
parents:
diff changeset
285 return SImode;
kono
parents:
diff changeset
286 else if (bytes > GET_MODE_SIZE (HImode))
kono
parents:
diff changeset
287 return HImode;
kono
parents:
diff changeset
288
kono
parents:
diff changeset
289 /* final fallback is do one byte */
kono
parents:
diff changeset
290 return QImode;
kono
parents:
diff changeset
291 }
kono
parents:
diff changeset
292
kono
parents:
diff changeset
293 /* Compute the alignment of pointer+OFFSET where the original alignment
kono
parents:
diff changeset
294 of pointer was BASE_ALIGN. */
kono
parents:
diff changeset
295 static unsigned HOST_WIDE_INT
kono
parents:
diff changeset
296 compute_current_alignment (unsigned HOST_WIDE_INT base_align,
kono
parents:
diff changeset
297 unsigned HOST_WIDE_INT offset)
kono
parents:
diff changeset
298 {
kono
parents:
diff changeset
299 if (offset == 0)
kono
parents:
diff changeset
300 return base_align;
kono
parents:
diff changeset
301 return MIN (base_align, offset & -offset);
kono
parents:
diff changeset
302 }
kono
parents:
diff changeset
303
kono
parents:
diff changeset
304 /* Expand a block compare operation, and return true if successful.
kono
parents:
diff changeset
305 Return false if we should let the compiler generate normal code,
kono
parents:
diff changeset
306 probably a memcmp call.
kono
parents:
diff changeset
307
kono
parents:
diff changeset
308 OPERANDS[0] is the target (result).
kono
parents:
diff changeset
309 OPERANDS[1] is the first source.
kono
parents:
diff changeset
310 OPERANDS[2] is the second source.
kono
parents:
diff changeset
311 OPERANDS[3] is the length.
kono
parents:
diff changeset
312 OPERANDS[4] is the alignment. */
kono
parents:
diff changeset
313 bool
kono
parents:
diff changeset
314 expand_block_compare (rtx operands[])
kono
parents:
diff changeset
315 {
kono
parents:
diff changeset
316 rtx target = operands[0];
kono
parents:
diff changeset
317 rtx orig_src1 = operands[1];
kono
parents:
diff changeset
318 rtx orig_src2 = operands[2];
kono
parents:
diff changeset
319 rtx bytes_rtx = operands[3];
kono
parents:
diff changeset
320 rtx align_rtx = operands[4];
kono
parents:
diff changeset
321 HOST_WIDE_INT cmp_bytes = 0;
kono
parents:
diff changeset
322 rtx src1 = orig_src1;
kono
parents:
diff changeset
323 rtx src2 = orig_src2;
kono
parents:
diff changeset
324
kono
parents:
diff changeset
325 /* This case is complicated to handle because the subtract
kono
parents:
diff changeset
326 with carry instructions do not generate the 64-bit
kono
parents:
diff changeset
327 carry and so we must emit code to calculate it ourselves.
kono
parents:
diff changeset
328 We choose not to implement this yet. */
kono
parents:
diff changeset
329 if (TARGET_32BIT && TARGET_POWERPC64)
kono
parents:
diff changeset
330 return false;
kono
parents:
diff changeset
331
kono
parents:
diff changeset
332 /* If this is not a fixed size compare, just call memcmp. */
kono
parents:
diff changeset
333 if (!CONST_INT_P (bytes_rtx))
kono
parents:
diff changeset
334 return false;
kono
parents:
diff changeset
335
kono
parents:
diff changeset
336 /* This must be a fixed size alignment. */
kono
parents:
diff changeset
337 if (!CONST_INT_P (align_rtx))
kono
parents:
diff changeset
338 return false;
kono
parents:
diff changeset
339
kono
parents:
diff changeset
340 unsigned int base_align = UINTVAL (align_rtx) / BITS_PER_UNIT;
kono
parents:
diff changeset
341
kono
parents:
diff changeset
342 /* targetm.slow_unaligned_access -- don't do unaligned stuff. */
kono
parents:
diff changeset
343 if (targetm.slow_unaligned_access (word_mode, MEM_ALIGN (orig_src1))
kono
parents:
diff changeset
344 || targetm.slow_unaligned_access (word_mode, MEM_ALIGN (orig_src2)))
kono
parents:
diff changeset
345 return false;
kono
parents:
diff changeset
346
kono
parents:
diff changeset
347 gcc_assert (GET_MODE (target) == SImode);
kono
parents:
diff changeset
348
kono
parents:
diff changeset
349 /* Anything to move? */
kono
parents:
diff changeset
350 unsigned HOST_WIDE_INT bytes = UINTVAL (bytes_rtx);
kono
parents:
diff changeset
351 if (bytes == 0)
kono
parents:
diff changeset
352 return true;
kono
parents:
diff changeset
353
kono
parents:
diff changeset
354 /* The code generated for p7 and older is not faster than glibc
kono
parents:
diff changeset
355 memcmp if alignment is small and length is not short, so bail
kono
parents:
diff changeset
356 out to avoid those conditions. */
kono
parents:
diff changeset
357 if (!TARGET_EFFICIENT_OVERLAPPING_UNALIGNED
kono
parents:
diff changeset
358 && ((base_align == 1 && bytes > 16)
kono
parents:
diff changeset
359 || (base_align == 2 && bytes > 32)))
kono
parents:
diff changeset
360 return false;
kono
parents:
diff changeset
361
kono
parents:
diff changeset
362 rtx tmp_reg_src1 = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
363 rtx tmp_reg_src2 = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
364 /* P7/P8 code uses cond for subfc. but P9 uses
kono
parents:
diff changeset
365 it for cmpld which needs CCUNSmode. */
kono
parents:
diff changeset
366 rtx cond;
kono
parents:
diff changeset
367 if (TARGET_P9_MISC)
kono
parents:
diff changeset
368 cond = gen_reg_rtx (CCUNSmode);
kono
parents:
diff changeset
369 else
kono
parents:
diff changeset
370 cond = gen_reg_rtx (CCmode);
kono
parents:
diff changeset
371
kono
parents:
diff changeset
372 /* If we have an LE target without ldbrx and word_mode is DImode,
kono
parents:
diff changeset
373 then we must avoid using word_mode. */
kono
parents:
diff changeset
374 int word_mode_ok = !(!BYTES_BIG_ENDIAN && !TARGET_LDBRX
kono
parents:
diff changeset
375 && word_mode == DImode);
kono
parents:
diff changeset
376
kono
parents:
diff changeset
377 /* Strategy phase. How many ops will this take and should we expand it? */
kono
parents:
diff changeset
378
kono
parents:
diff changeset
379 unsigned HOST_WIDE_INT offset = 0;
kono
parents:
diff changeset
380 machine_mode load_mode =
kono
parents:
diff changeset
381 select_block_compare_mode (offset, bytes, base_align, word_mode_ok);
kono
parents:
diff changeset
382 unsigned int load_mode_size = GET_MODE_SIZE (load_mode);
kono
parents:
diff changeset
383
kono
parents:
diff changeset
384 /* We don't want to generate too much code. */
kono
parents:
diff changeset
385 unsigned HOST_WIDE_INT max_bytes =
kono
parents:
diff changeset
386 load_mode_size * (unsigned HOST_WIDE_INT) rs6000_block_compare_inline_limit;
kono
parents:
diff changeset
387 if (!IN_RANGE (bytes, 1, max_bytes))
kono
parents:
diff changeset
388 return false;
kono
parents:
diff changeset
389
kono
parents:
diff changeset
390 bool generate_6432_conversion = false;
kono
parents:
diff changeset
391 rtx convert_label = NULL;
kono
parents:
diff changeset
392 rtx final_label = NULL;
kono
parents:
diff changeset
393
kono
parents:
diff changeset
394 /* Example of generated code for 18 bytes aligned 1 byte.
kono
parents:
diff changeset
395 Compiled with -fno-reorder-blocks for clarity.
kono
parents:
diff changeset
396 ldbrx 10,31,8
kono
parents:
diff changeset
397 ldbrx 9,7,8
kono
parents:
diff changeset
398 subfc. 9,9,10
kono
parents:
diff changeset
399 bne 0,.L6487
kono
parents:
diff changeset
400 addi 9,12,8
kono
parents:
diff changeset
401 addi 5,11,8
kono
parents:
diff changeset
402 ldbrx 10,0,9
kono
parents:
diff changeset
403 ldbrx 9,0,5
kono
parents:
diff changeset
404 subfc. 9,9,10
kono
parents:
diff changeset
405 bne 0,.L6487
kono
parents:
diff changeset
406 addi 9,12,16
kono
parents:
diff changeset
407 lhbrx 10,0,9
kono
parents:
diff changeset
408 addi 9,11,16
kono
parents:
diff changeset
409 lhbrx 9,0,9
kono
parents:
diff changeset
410 subf 9,9,10
kono
parents:
diff changeset
411 b .L6488
kono
parents:
diff changeset
412 .p2align 4,,15
kono
parents:
diff changeset
413 .L6487: #convert_label
kono
parents:
diff changeset
414 popcntd 9,9
kono
parents:
diff changeset
415 subfe 10,10,10
kono
parents:
diff changeset
416 or 9,9,10
kono
parents:
diff changeset
417 .L6488: #final_label
kono
parents:
diff changeset
418 extsw 10,9
kono
parents:
diff changeset
419
kono
parents:
diff changeset
420 We start off with DImode for two blocks that jump to the DI->SI conversion
kono
parents:
diff changeset
421 if the difference is found there, then a final block of HImode that skips
kono
parents:
diff changeset
422 the DI->SI conversion. */
kono
parents:
diff changeset
423
kono
parents:
diff changeset
424 while (bytes > 0)
kono
parents:
diff changeset
425 {
kono
parents:
diff changeset
426 unsigned int align = compute_current_alignment (base_align, offset);
kono
parents:
diff changeset
427 if (TARGET_EFFICIENT_OVERLAPPING_UNALIGNED)
kono
parents:
diff changeset
428 load_mode = select_block_compare_mode (offset, bytes, align,
kono
parents:
diff changeset
429 word_mode_ok);
kono
parents:
diff changeset
430 else
kono
parents:
diff changeset
431 load_mode = select_block_compare_mode (0, bytes, align, word_mode_ok);
kono
parents:
diff changeset
432 load_mode_size = GET_MODE_SIZE (load_mode);
kono
parents:
diff changeset
433 if (bytes >= load_mode_size)
kono
parents:
diff changeset
434 cmp_bytes = load_mode_size;
kono
parents:
diff changeset
435 else if (TARGET_EFFICIENT_OVERLAPPING_UNALIGNED)
kono
parents:
diff changeset
436 {
kono
parents:
diff changeset
437 /* Move this load back so it doesn't go past the end.
kono
parents:
diff changeset
438 P8/P9 can do this efficiently. */
kono
parents:
diff changeset
439 unsigned int extra_bytes = load_mode_size - bytes;
kono
parents:
diff changeset
440 cmp_bytes = bytes;
kono
parents:
diff changeset
441 if (extra_bytes < offset)
kono
parents:
diff changeset
442 {
kono
parents:
diff changeset
443 offset -= extra_bytes;
kono
parents:
diff changeset
444 cmp_bytes = load_mode_size;
kono
parents:
diff changeset
445 bytes = cmp_bytes;
kono
parents:
diff changeset
446 }
kono
parents:
diff changeset
447 }
kono
parents:
diff changeset
448 else
kono
parents:
diff changeset
449 /* P7 and earlier can't do the overlapping load trick fast,
kono
parents:
diff changeset
450 so this forces a non-overlapping load and a shift to get
kono
parents:
diff changeset
451 rid of the extra bytes. */
kono
parents:
diff changeset
452 cmp_bytes = bytes;
kono
parents:
diff changeset
453
kono
parents:
diff changeset
454 src1 = adjust_address (orig_src1, load_mode, offset);
kono
parents:
diff changeset
455 src2 = adjust_address (orig_src2, load_mode, offset);
kono
parents:
diff changeset
456
kono
parents:
diff changeset
457 if (!REG_P (XEXP (src1, 0)))
kono
parents:
diff changeset
458 {
kono
parents:
diff changeset
459 rtx src1_reg = copy_addr_to_reg (XEXP (src1, 0));
kono
parents:
diff changeset
460 src1 = replace_equiv_address (src1, src1_reg);
kono
parents:
diff changeset
461 }
kono
parents:
diff changeset
462 set_mem_size (src1, cmp_bytes);
kono
parents:
diff changeset
463
kono
parents:
diff changeset
464 if (!REG_P (XEXP (src2, 0)))
kono
parents:
diff changeset
465 {
kono
parents:
diff changeset
466 rtx src2_reg = copy_addr_to_reg (XEXP (src2, 0));
kono
parents:
diff changeset
467 src2 = replace_equiv_address (src2, src2_reg);
kono
parents:
diff changeset
468 }
kono
parents:
diff changeset
469 set_mem_size (src2, cmp_bytes);
kono
parents:
diff changeset
470
kono
parents:
diff changeset
471 do_load_for_compare (tmp_reg_src1, src1, load_mode);
kono
parents:
diff changeset
472 do_load_for_compare (tmp_reg_src2, src2, load_mode);
kono
parents:
diff changeset
473
kono
parents:
diff changeset
474 if (cmp_bytes < load_mode_size)
kono
parents:
diff changeset
475 {
kono
parents:
diff changeset
476 /* Shift unneeded bytes off. */
kono
parents:
diff changeset
477 rtx sh = GEN_INT (BITS_PER_UNIT * (load_mode_size - cmp_bytes));
kono
parents:
diff changeset
478 if (word_mode == DImode)
kono
parents:
diff changeset
479 {
kono
parents:
diff changeset
480 emit_insn (gen_lshrdi3 (tmp_reg_src1, tmp_reg_src1, sh));
kono
parents:
diff changeset
481 emit_insn (gen_lshrdi3 (tmp_reg_src2, tmp_reg_src2, sh));
kono
parents:
diff changeset
482 }
kono
parents:
diff changeset
483 else
kono
parents:
diff changeset
484 {
kono
parents:
diff changeset
485 emit_insn (gen_lshrsi3 (tmp_reg_src1, tmp_reg_src1, sh));
kono
parents:
diff changeset
486 emit_insn (gen_lshrsi3 (tmp_reg_src2, tmp_reg_src2, sh));
kono
parents:
diff changeset
487 }
kono
parents:
diff changeset
488 }
kono
parents:
diff changeset
489
kono
parents:
diff changeset
490 int remain = bytes - cmp_bytes;
kono
parents:
diff changeset
491 if (GET_MODE_SIZE (GET_MODE (target)) > GET_MODE_SIZE (load_mode))
kono
parents:
diff changeset
492 {
kono
parents:
diff changeset
493 /* Target is larger than load size so we don't need to
kono
parents:
diff changeset
494 reduce result size. */
kono
parents:
diff changeset
495
kono
parents:
diff changeset
496 /* We previously did a block that need 64->32 conversion but
kono
parents:
diff changeset
497 the current block does not, so a label is needed to jump
kono
parents:
diff changeset
498 to the end. */
kono
parents:
diff changeset
499 if (generate_6432_conversion && !final_label)
kono
parents:
diff changeset
500 final_label = gen_label_rtx ();
kono
parents:
diff changeset
501
kono
parents:
diff changeset
502 if (remain > 0)
kono
parents:
diff changeset
503 {
kono
parents:
diff changeset
504 /* This is not the last block, branch to the end if the result
kono
parents:
diff changeset
505 of this subtract is not zero. */
kono
parents:
diff changeset
506 if (!final_label)
kono
parents:
diff changeset
507 final_label = gen_label_rtx ();
kono
parents:
diff changeset
508 rtx fin_ref = gen_rtx_LABEL_REF (VOIDmode, final_label);
kono
parents:
diff changeset
509 rtx tmp = gen_rtx_MINUS (word_mode, tmp_reg_src1, tmp_reg_src2);
kono
parents:
diff changeset
510 rtx cr = gen_reg_rtx (CCmode);
kono
parents:
diff changeset
511 rs6000_emit_dot_insn (tmp_reg_src2, tmp, 2, cr);
kono
parents:
diff changeset
512 emit_insn (gen_movsi (target,
kono
parents:
diff changeset
513 gen_lowpart (SImode, tmp_reg_src2)));
kono
parents:
diff changeset
514 rtx ne_rtx = gen_rtx_NE (VOIDmode, cr, const0_rtx);
kono
parents:
diff changeset
515 rtx ifelse = gen_rtx_IF_THEN_ELSE (VOIDmode, ne_rtx,
kono
parents:
diff changeset
516 fin_ref, pc_rtx);
kono
parents:
diff changeset
517 rtx j = emit_jump_insn (gen_rtx_SET (pc_rtx, ifelse));
kono
parents:
diff changeset
518 JUMP_LABEL (j) = final_label;
kono
parents:
diff changeset
519 LABEL_NUSES (final_label) += 1;
kono
parents:
diff changeset
520 }
kono
parents:
diff changeset
521 else
kono
parents:
diff changeset
522 {
kono
parents:
diff changeset
523 if (word_mode == DImode)
kono
parents:
diff changeset
524 {
kono
parents:
diff changeset
525 emit_insn (gen_subdi3 (tmp_reg_src2, tmp_reg_src1,
kono
parents:
diff changeset
526 tmp_reg_src2));
kono
parents:
diff changeset
527 emit_insn (gen_movsi (target,
kono
parents:
diff changeset
528 gen_lowpart (SImode, tmp_reg_src2)));
kono
parents:
diff changeset
529 }
kono
parents:
diff changeset
530 else
kono
parents:
diff changeset
531 emit_insn (gen_subsi3 (target, tmp_reg_src1, tmp_reg_src2));
kono
parents:
diff changeset
532
kono
parents:
diff changeset
533 if (final_label)
kono
parents:
diff changeset
534 {
kono
parents:
diff changeset
535 rtx fin_ref = gen_rtx_LABEL_REF (VOIDmode, final_label);
kono
parents:
diff changeset
536 rtx j = emit_jump_insn (gen_rtx_SET (pc_rtx, fin_ref));
kono
parents:
diff changeset
537 JUMP_LABEL(j) = final_label;
kono
parents:
diff changeset
538 LABEL_NUSES (final_label) += 1;
kono
parents:
diff changeset
539 emit_barrier ();
kono
parents:
diff changeset
540 }
kono
parents:
diff changeset
541 }
kono
parents:
diff changeset
542 }
kono
parents:
diff changeset
543 else
kono
parents:
diff changeset
544 {
kono
parents:
diff changeset
545 /* Do we need a 64->32 conversion block? We need the 64->32
kono
parents:
diff changeset
546 conversion even if target size == load_mode size because
kono
parents:
diff changeset
547 the subtract generates one extra bit. */
kono
parents:
diff changeset
548 generate_6432_conversion = true;
kono
parents:
diff changeset
549
kono
parents:
diff changeset
550 if (remain > 0)
kono
parents:
diff changeset
551 {
kono
parents:
diff changeset
552 if (!convert_label)
kono
parents:
diff changeset
553 convert_label = gen_label_rtx ();
kono
parents:
diff changeset
554
kono
parents:
diff changeset
555 /* Compare to zero and branch to convert_label if not zero. */
kono
parents:
diff changeset
556 rtx cvt_ref = gen_rtx_LABEL_REF (VOIDmode, convert_label);
kono
parents:
diff changeset
557 if (TARGET_P9_MISC)
kono
parents:
diff changeset
558 {
kono
parents:
diff changeset
559 /* Generate a compare, and convert with a setb later. */
kono
parents:
diff changeset
560 rtx cmp = gen_rtx_COMPARE (CCUNSmode, tmp_reg_src1,
kono
parents:
diff changeset
561 tmp_reg_src2);
kono
parents:
diff changeset
562 emit_insn (gen_rtx_SET (cond, cmp));
kono
parents:
diff changeset
563 }
kono
parents:
diff changeset
564 else
kono
parents:
diff changeset
565 /* Generate a subfc. and use the longer
kono
parents:
diff changeset
566 sequence for conversion. */
kono
parents:
diff changeset
567 if (TARGET_64BIT)
kono
parents:
diff changeset
568 emit_insn (gen_subfdi3_carry_dot2 (tmp_reg_src2, tmp_reg_src2,
kono
parents:
diff changeset
569 tmp_reg_src1, cond));
kono
parents:
diff changeset
570 else
kono
parents:
diff changeset
571 emit_insn (gen_subfsi3_carry_dot2 (tmp_reg_src2, tmp_reg_src2,
kono
parents:
diff changeset
572 tmp_reg_src1, cond));
kono
parents:
diff changeset
573 rtx ne_rtx = gen_rtx_NE (VOIDmode, cond, const0_rtx);
kono
parents:
diff changeset
574 rtx ifelse = gen_rtx_IF_THEN_ELSE (VOIDmode, ne_rtx,
kono
parents:
diff changeset
575 cvt_ref, pc_rtx);
kono
parents:
diff changeset
576 rtx j = emit_jump_insn (gen_rtx_SET (pc_rtx, ifelse));
kono
parents:
diff changeset
577 JUMP_LABEL(j) = convert_label;
kono
parents:
diff changeset
578 LABEL_NUSES (convert_label) += 1;
kono
parents:
diff changeset
579 }
kono
parents:
diff changeset
580 else
kono
parents:
diff changeset
581 {
kono
parents:
diff changeset
582 /* Just do the subtract/compare. Since this is the last block
kono
parents:
diff changeset
583 the convert code will be generated immediately following. */
kono
parents:
diff changeset
584 if (TARGET_P9_MISC)
kono
parents:
diff changeset
585 {
kono
parents:
diff changeset
586 rtx cmp = gen_rtx_COMPARE (CCUNSmode, tmp_reg_src1,
kono
parents:
diff changeset
587 tmp_reg_src2);
kono
parents:
diff changeset
588 emit_insn (gen_rtx_SET (cond, cmp));
kono
parents:
diff changeset
589 }
kono
parents:
diff changeset
590 else
kono
parents:
diff changeset
591 if (TARGET_64BIT)
kono
parents:
diff changeset
592 emit_insn (gen_subfdi3_carry (tmp_reg_src2, tmp_reg_src2,
kono
parents:
diff changeset
593 tmp_reg_src1));
kono
parents:
diff changeset
594 else
kono
parents:
diff changeset
595 emit_insn (gen_subfsi3_carry (tmp_reg_src2, tmp_reg_src2,
kono
parents:
diff changeset
596 tmp_reg_src1));
kono
parents:
diff changeset
597 }
kono
parents:
diff changeset
598 }
kono
parents:
diff changeset
599
kono
parents:
diff changeset
600 offset += cmp_bytes;
kono
parents:
diff changeset
601 bytes -= cmp_bytes;
kono
parents:
diff changeset
602 }
kono
parents:
diff changeset
603
kono
parents:
diff changeset
604 if (generate_6432_conversion)
kono
parents:
diff changeset
605 {
kono
parents:
diff changeset
606 if (convert_label)
kono
parents:
diff changeset
607 emit_label (convert_label);
kono
parents:
diff changeset
608
kono
parents:
diff changeset
609 /* We need to produce DI result from sub, then convert to target SI
kono
parents:
diff changeset
610 while maintaining <0 / ==0 / >0 properties. This sequence works:
kono
parents:
diff changeset
611 subfc L,A,B
kono
parents:
diff changeset
612 subfe H,H,H
kono
parents:
diff changeset
613 popcntd L,L
kono
parents:
diff changeset
614 rldimi L,H,6,0
kono
parents:
diff changeset
615
kono
parents:
diff changeset
616 This is an alternate one Segher cooked up if somebody
kono
parents:
diff changeset
617 wants to expand this for something that doesn't have popcntd:
kono
parents:
diff changeset
618 subfc L,a,b
kono
parents:
diff changeset
619 subfe H,x,x
kono
parents:
diff changeset
620 addic t,L,-1
kono
parents:
diff changeset
621 subfe v,t,L
kono
parents:
diff changeset
622 or z,v,H
kono
parents:
diff changeset
623
kono
parents:
diff changeset
624 And finally, p9 can just do this:
kono
parents:
diff changeset
625 cmpld A,B
kono
parents:
diff changeset
626 setb r */
kono
parents:
diff changeset
627
kono
parents:
diff changeset
628 if (TARGET_P9_MISC)
kono
parents:
diff changeset
629 {
kono
parents:
diff changeset
630 emit_insn (gen_setb_unsigned (target, cond));
kono
parents:
diff changeset
631 }
kono
parents:
diff changeset
632 else
kono
parents:
diff changeset
633 {
kono
parents:
diff changeset
634 if (TARGET_64BIT)
kono
parents:
diff changeset
635 {
kono
parents:
diff changeset
636 rtx tmp_reg_ca = gen_reg_rtx (DImode);
kono
parents:
diff changeset
637 emit_insn (gen_subfdi3_carry_in_xx (tmp_reg_ca));
kono
parents:
diff changeset
638 emit_insn (gen_popcntddi2 (tmp_reg_src2, tmp_reg_src2));
kono
parents:
diff changeset
639 emit_insn (gen_iordi3 (tmp_reg_src2, tmp_reg_src2, tmp_reg_ca));
kono
parents:
diff changeset
640 emit_insn (gen_movsi (target, gen_lowpart (SImode, tmp_reg_src2)));
kono
parents:
diff changeset
641 }
kono
parents:
diff changeset
642 else
kono
parents:
diff changeset
643 {
kono
parents:
diff changeset
644 rtx tmp_reg_ca = gen_reg_rtx (SImode);
kono
parents:
diff changeset
645 emit_insn (gen_subfsi3_carry_in_xx (tmp_reg_ca));
kono
parents:
diff changeset
646 emit_insn (gen_popcntdsi2 (tmp_reg_src2, tmp_reg_src2));
kono
parents:
diff changeset
647 emit_insn (gen_iorsi3 (target, tmp_reg_src2, tmp_reg_ca));
kono
parents:
diff changeset
648 }
kono
parents:
diff changeset
649 }
kono
parents:
diff changeset
650 }
kono
parents:
diff changeset
651
kono
parents:
diff changeset
652 if (final_label)
kono
parents:
diff changeset
653 emit_label (final_label);
kono
parents:
diff changeset
654
kono
parents:
diff changeset
655 gcc_assert (bytes == 0);
kono
parents:
diff changeset
656 return true;
kono
parents:
diff changeset
657 }
kono
parents:
diff changeset
658
kono
parents:
diff changeset
659 /* Generate alignment check and branch code to set up for
kono
parents:
diff changeset
660 strncmp when we don't have DI alignment.
kono
parents:
diff changeset
661 STRNCMP_LABEL is the label to branch if there is a page crossing.
kono
parents:
diff changeset
662 SRC is the string pointer to be examined.
kono
parents:
diff changeset
663 BYTES is the max number of bytes to compare. */
kono
parents:
diff changeset
664 static void
kono
parents:
diff changeset
665 expand_strncmp_align_check (rtx strncmp_label, rtx src, HOST_WIDE_INT bytes)
kono
parents:
diff changeset
666 {
kono
parents:
diff changeset
667 rtx lab_ref = gen_rtx_LABEL_REF (VOIDmode, strncmp_label);
kono
parents:
diff changeset
668 rtx src_check = copy_addr_to_reg (XEXP (src, 0));
kono
parents:
diff changeset
669 if (GET_MODE (src_check) == SImode)
kono
parents:
diff changeset
670 emit_insn (gen_andsi3 (src_check, src_check, GEN_INT (0xfff)));
kono
parents:
diff changeset
671 else
kono
parents:
diff changeset
672 emit_insn (gen_anddi3 (src_check, src_check, GEN_INT (0xfff)));
kono
parents:
diff changeset
673 rtx cond = gen_reg_rtx (CCmode);
kono
parents:
diff changeset
674 emit_move_insn (cond, gen_rtx_COMPARE (CCmode, src_check,
kono
parents:
diff changeset
675 GEN_INT (4096 - bytes)));
kono
parents:
diff changeset
676
kono
parents:
diff changeset
677 rtx cmp_rtx = gen_rtx_GE (VOIDmode, cond, const0_rtx);
kono
parents:
diff changeset
678
kono
parents:
diff changeset
679 rtx ifelse = gen_rtx_IF_THEN_ELSE (VOIDmode, cmp_rtx,
kono
parents:
diff changeset
680 lab_ref, pc_rtx);
kono
parents:
diff changeset
681 rtx j = emit_jump_insn (gen_rtx_SET (pc_rtx, ifelse));
kono
parents:
diff changeset
682 JUMP_LABEL (j) = strncmp_label;
kono
parents:
diff changeset
683 LABEL_NUSES (strncmp_label) += 1;
kono
parents:
diff changeset
684 }
kono
parents:
diff changeset
685
kono
parents:
diff changeset
686 /* Expand a string compare operation with length, and return
kono
parents:
diff changeset
687 true if successful. Return false if we should let the
kono
parents:
diff changeset
688 compiler generate normal code, probably a strncmp call.
kono
parents:
diff changeset
689
kono
parents:
diff changeset
690 OPERANDS[0] is the target (result).
kono
parents:
diff changeset
691 OPERANDS[1] is the first source.
kono
parents:
diff changeset
692 OPERANDS[2] is the second source.
kono
parents:
diff changeset
693 If NO_LENGTH is zero, then:
kono
parents:
diff changeset
694 OPERANDS[3] is the length.
kono
parents:
diff changeset
695 OPERANDS[4] is the alignment in bytes.
kono
parents:
diff changeset
696 If NO_LENGTH is nonzero, then:
kono
parents:
diff changeset
697 OPERANDS[3] is the alignment in bytes. */
kono
parents:
diff changeset
698 bool
kono
parents:
diff changeset
699 expand_strn_compare (rtx operands[], int no_length)
kono
parents:
diff changeset
700 {
kono
parents:
diff changeset
701 rtx target = operands[0];
kono
parents:
diff changeset
702 rtx orig_src1 = operands[1];
kono
parents:
diff changeset
703 rtx orig_src2 = operands[2];
kono
parents:
diff changeset
704 rtx bytes_rtx, align_rtx;
kono
parents:
diff changeset
705 if (no_length)
kono
parents:
diff changeset
706 {
kono
parents:
diff changeset
707 bytes_rtx = NULL;
kono
parents:
diff changeset
708 align_rtx = operands[3];
kono
parents:
diff changeset
709 }
kono
parents:
diff changeset
710 else
kono
parents:
diff changeset
711 {
kono
parents:
diff changeset
712 bytes_rtx = operands[3];
kono
parents:
diff changeset
713 align_rtx = operands[4];
kono
parents:
diff changeset
714 }
kono
parents:
diff changeset
715 unsigned HOST_WIDE_INT cmp_bytes = 0;
kono
parents:
diff changeset
716 rtx src1 = orig_src1;
kono
parents:
diff changeset
717 rtx src2 = orig_src2;
kono
parents:
diff changeset
718
kono
parents:
diff changeset
719 /* If we have a length, it must be constant. This simplifies things
kono
parents:
diff changeset
720 a bit as we don't have to generate code to check if we've exceeded
kono
parents:
diff changeset
721 the length. Later this could be expanded to handle this case. */
kono
parents:
diff changeset
722 if (!no_length && !CONST_INT_P (bytes_rtx))
kono
parents:
diff changeset
723 return false;
kono
parents:
diff changeset
724
kono
parents:
diff changeset
725 /* This must be a fixed size alignment. */
kono
parents:
diff changeset
726 if (!CONST_INT_P (align_rtx))
kono
parents:
diff changeset
727 return false;
kono
parents:
diff changeset
728
kono
parents:
diff changeset
729 unsigned int base_align = UINTVAL (align_rtx);
kono
parents:
diff changeset
730 int align1 = MEM_ALIGN (orig_src1) / BITS_PER_UNIT;
kono
parents:
diff changeset
731 int align2 = MEM_ALIGN (orig_src2) / BITS_PER_UNIT;
kono
parents:
diff changeset
732
kono
parents:
diff changeset
733 /* targetm.slow_unaligned_access -- don't do unaligned stuff. */
kono
parents:
diff changeset
734 if (targetm.slow_unaligned_access (word_mode, align1)
kono
parents:
diff changeset
735 || targetm.slow_unaligned_access (word_mode, align2))
kono
parents:
diff changeset
736 return false;
kono
parents:
diff changeset
737
kono
parents:
diff changeset
738 gcc_assert (GET_MODE (target) == SImode);
kono
parents:
diff changeset
739
kono
parents:
diff changeset
740 /* If we have an LE target without ldbrx and word_mode is DImode,
kono
parents:
diff changeset
741 then we must avoid using word_mode. */
kono
parents:
diff changeset
742 int word_mode_ok = !(!BYTES_BIG_ENDIAN && !TARGET_LDBRX
kono
parents:
diff changeset
743 && word_mode == DImode);
kono
parents:
diff changeset
744
kono
parents:
diff changeset
745 unsigned int word_mode_size = GET_MODE_SIZE (word_mode);
kono
parents:
diff changeset
746
kono
parents:
diff changeset
747 unsigned HOST_WIDE_INT offset = 0;
kono
parents:
diff changeset
748 unsigned HOST_WIDE_INT bytes; /* N from the strncmp args if available. */
kono
parents:
diff changeset
749 unsigned HOST_WIDE_INT compare_length; /* How much to compare inline. */
kono
parents:
diff changeset
750 if (no_length)
kono
parents:
diff changeset
751 /* Use this as a standin to determine the mode to use. */
kono
parents:
diff changeset
752 bytes = rs6000_string_compare_inline_limit * word_mode_size;
kono
parents:
diff changeset
753 else
kono
parents:
diff changeset
754 bytes = UINTVAL (bytes_rtx);
kono
parents:
diff changeset
755
kono
parents:
diff changeset
756 machine_mode load_mode =
kono
parents:
diff changeset
757 select_block_compare_mode (offset, bytes, base_align, word_mode_ok);
kono
parents:
diff changeset
758 unsigned int load_mode_size = GET_MODE_SIZE (load_mode);
kono
parents:
diff changeset
759 compare_length = rs6000_string_compare_inline_limit * load_mode_size;
kono
parents:
diff changeset
760
kono
parents:
diff changeset
761 /* If we have equality at the end of the last compare and we have not
kono
parents:
diff changeset
762 found the end of the string, we need to call strcmp/strncmp to
kono
parents:
diff changeset
763 compare the remainder. */
kono
parents:
diff changeset
764 bool equality_compare_rest = false;
kono
parents:
diff changeset
765
kono
parents:
diff changeset
766 if (no_length)
kono
parents:
diff changeset
767 {
kono
parents:
diff changeset
768 bytes = compare_length;
kono
parents:
diff changeset
769 equality_compare_rest = true;
kono
parents:
diff changeset
770 }
kono
parents:
diff changeset
771 else
kono
parents:
diff changeset
772 {
kono
parents:
diff changeset
773 if (bytes <= compare_length)
kono
parents:
diff changeset
774 compare_length = bytes;
kono
parents:
diff changeset
775 else
kono
parents:
diff changeset
776 equality_compare_rest = true;
kono
parents:
diff changeset
777 }
kono
parents:
diff changeset
778
kono
parents:
diff changeset
779 rtx result_reg = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
780 rtx final_move_label = gen_label_rtx ();
kono
parents:
diff changeset
781 rtx final_label = gen_label_rtx ();
kono
parents:
diff changeset
782 rtx begin_compare_label = NULL;
kono
parents:
diff changeset
783
kono
parents:
diff changeset
784 if (base_align < 8)
kono
parents:
diff changeset
785 {
kono
parents:
diff changeset
786 /* Generate code that checks distance to 4k boundary for this case. */
kono
parents:
diff changeset
787 begin_compare_label = gen_label_rtx ();
kono
parents:
diff changeset
788 rtx strncmp_label = gen_label_rtx ();
kono
parents:
diff changeset
789 rtx jmp;
kono
parents:
diff changeset
790
kono
parents:
diff changeset
791 /* Strncmp for power8 in glibc does this:
kono
parents:
diff changeset
792 rldicl r8,r3,0,52
kono
parents:
diff changeset
793 cmpldi cr7,r8,4096-16
kono
parents:
diff changeset
794 bgt cr7,L(pagecross) */
kono
parents:
diff changeset
795
kono
parents:
diff changeset
796 /* Make sure that the length we use for the alignment test and
kono
parents:
diff changeset
797 the subsequent code generation are in agreement so we do not
kono
parents:
diff changeset
798 go past the length we tested for a 4k boundary crossing. */
kono
parents:
diff changeset
799 unsigned HOST_WIDE_INT align_test = compare_length;
kono
parents:
diff changeset
800 if (align_test < 8)
kono
parents:
diff changeset
801 {
kono
parents:
diff changeset
802 align_test = HOST_WIDE_INT_1U << ceil_log2 (align_test);
kono
parents:
diff changeset
803 base_align = align_test;
kono
parents:
diff changeset
804 }
kono
parents:
diff changeset
805 else
kono
parents:
diff changeset
806 {
kono
parents:
diff changeset
807 align_test = ROUND_UP (align_test, 8);
kono
parents:
diff changeset
808 base_align = 8;
kono
parents:
diff changeset
809 }
kono
parents:
diff changeset
810
kono
parents:
diff changeset
811 if (align1 < 8)
kono
parents:
diff changeset
812 expand_strncmp_align_check (strncmp_label, src1, align_test);
kono
parents:
diff changeset
813 if (align2 < 8)
kono
parents:
diff changeset
814 expand_strncmp_align_check (strncmp_label, src2, align_test);
kono
parents:
diff changeset
815
kono
parents:
diff changeset
816 /* Now generate the following sequence:
kono
parents:
diff changeset
817 - branch to begin_compare
kono
parents:
diff changeset
818 - strncmp_label
kono
parents:
diff changeset
819 - call to strncmp
kono
parents:
diff changeset
820 - branch to final_label
kono
parents:
diff changeset
821 - begin_compare_label */
kono
parents:
diff changeset
822
kono
parents:
diff changeset
823 rtx cmp_ref = gen_rtx_LABEL_REF (VOIDmode, begin_compare_label);
kono
parents:
diff changeset
824 jmp = emit_jump_insn (gen_rtx_SET (pc_rtx, cmp_ref));
kono
parents:
diff changeset
825 JUMP_LABEL (jmp) = begin_compare_label;
kono
parents:
diff changeset
826 LABEL_NUSES (begin_compare_label) += 1;
kono
parents:
diff changeset
827 emit_barrier ();
kono
parents:
diff changeset
828
kono
parents:
diff changeset
829 emit_label (strncmp_label);
kono
parents:
diff changeset
830
kono
parents:
diff changeset
831 if (!REG_P (XEXP (src1, 0)))
kono
parents:
diff changeset
832 {
kono
parents:
diff changeset
833 rtx src1_reg = copy_addr_to_reg (XEXP (src1, 0));
kono
parents:
diff changeset
834 src1 = replace_equiv_address (src1, src1_reg);
kono
parents:
diff changeset
835 }
kono
parents:
diff changeset
836
kono
parents:
diff changeset
837 if (!REG_P (XEXP (src2, 0)))
kono
parents:
diff changeset
838 {
kono
parents:
diff changeset
839 rtx src2_reg = copy_addr_to_reg (XEXP (src2, 0));
kono
parents:
diff changeset
840 src2 = replace_equiv_address (src2, src2_reg);
kono
parents:
diff changeset
841 }
kono
parents:
diff changeset
842
kono
parents:
diff changeset
843 if (no_length)
kono
parents:
diff changeset
844 {
kono
parents:
diff changeset
845 tree fun = builtin_decl_explicit (BUILT_IN_STRCMP);
kono
parents:
diff changeset
846 emit_library_call_value (XEXP (DECL_RTL (fun), 0),
kono
parents:
diff changeset
847 target, LCT_NORMAL, GET_MODE (target),
kono
parents:
diff changeset
848 force_reg (Pmode, XEXP (src1, 0)), Pmode,
kono
parents:
diff changeset
849 force_reg (Pmode, XEXP (src2, 0)), Pmode);
kono
parents:
diff changeset
850 }
kono
parents:
diff changeset
851 else
kono
parents:
diff changeset
852 {
kono
parents:
diff changeset
853 /* -m32 -mpowerpc64 results in word_mode being DImode even
kono
parents:
diff changeset
854 though otherwise it is 32-bit. The length arg to strncmp
kono
parents:
diff changeset
855 is a size_t which will be the same size as pointers. */
kono
parents:
diff changeset
856 rtx len_rtx;
kono
parents:
diff changeset
857 if (TARGET_64BIT)
kono
parents:
diff changeset
858 len_rtx = gen_reg_rtx (DImode);
kono
parents:
diff changeset
859 else
kono
parents:
diff changeset
860 len_rtx = gen_reg_rtx (SImode);
kono
parents:
diff changeset
861
kono
parents:
diff changeset
862 emit_move_insn (len_rtx, bytes_rtx);
kono
parents:
diff changeset
863
kono
parents:
diff changeset
864 tree fun = builtin_decl_explicit (BUILT_IN_STRNCMP);
kono
parents:
diff changeset
865 emit_library_call_value (XEXP (DECL_RTL (fun), 0),
kono
parents:
diff changeset
866 target, LCT_NORMAL, GET_MODE (target),
kono
parents:
diff changeset
867 force_reg (Pmode, XEXP (src1, 0)), Pmode,
kono
parents:
diff changeset
868 force_reg (Pmode, XEXP (src2, 0)), Pmode,
kono
parents:
diff changeset
869 len_rtx, GET_MODE (len_rtx));
kono
parents:
diff changeset
870 }
kono
parents:
diff changeset
871
kono
parents:
diff changeset
872 rtx fin_ref = gen_rtx_LABEL_REF (VOIDmode, final_label);
kono
parents:
diff changeset
873 jmp = emit_jump_insn (gen_rtx_SET (pc_rtx, fin_ref));
kono
parents:
diff changeset
874 JUMP_LABEL (jmp) = final_label;
kono
parents:
diff changeset
875 LABEL_NUSES (final_label) += 1;
kono
parents:
diff changeset
876 emit_barrier ();
kono
parents:
diff changeset
877 emit_label (begin_compare_label);
kono
parents:
diff changeset
878 }
kono
parents:
diff changeset
879
kono
parents:
diff changeset
880 rtx cleanup_label = NULL;
kono
parents:
diff changeset
881 rtx tmp_reg_src1 = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
882 rtx tmp_reg_src2 = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
883
kono
parents:
diff changeset
884 /* Generate sequence of ld/ldbrx, cmpb to compare out
kono
parents:
diff changeset
885 to the length specified. */
kono
parents:
diff changeset
886 unsigned HOST_WIDE_INT bytes_to_compare = compare_length;
kono
parents:
diff changeset
887 while (bytes_to_compare > 0)
kono
parents:
diff changeset
888 {
kono
parents:
diff changeset
889 /* Compare sequence:
kono
parents:
diff changeset
890 check each 8B with: ld/ld cmpd bne
kono
parents:
diff changeset
891 If equal, use rldicr/cmpb to check for zero byte.
kono
parents:
diff changeset
892 cleanup code at end:
kono
parents:
diff changeset
893 cmpb get byte that differs
kono
parents:
diff changeset
894 cmpb look for zero byte
kono
parents:
diff changeset
895 orc combine
kono
parents:
diff changeset
896 cntlzd get bit of first zero/diff byte
kono
parents:
diff changeset
897 subfic convert for rldcl use
kono
parents:
diff changeset
898 rldcl rldcl extract diff/zero byte
kono
parents:
diff changeset
899 subf subtract for final result
kono
parents:
diff changeset
900
kono
parents:
diff changeset
901 The last compare can branch around the cleanup code if the
kono
parents:
diff changeset
902 result is zero because the strings are exactly equal. */
kono
parents:
diff changeset
903 unsigned int align = compute_current_alignment (base_align, offset);
kono
parents:
diff changeset
904 if (TARGET_EFFICIENT_OVERLAPPING_UNALIGNED)
kono
parents:
diff changeset
905 load_mode = select_block_compare_mode (offset, bytes_to_compare, align,
kono
parents:
diff changeset
906 word_mode_ok);
kono
parents:
diff changeset
907 else
kono
parents:
diff changeset
908 load_mode = select_block_compare_mode (0, bytes_to_compare, align,
kono
parents:
diff changeset
909 word_mode_ok);
kono
parents:
diff changeset
910 load_mode_size = GET_MODE_SIZE (load_mode);
kono
parents:
diff changeset
911 if (bytes_to_compare >= load_mode_size)
kono
parents:
diff changeset
912 cmp_bytes = load_mode_size;
kono
parents:
diff changeset
913 else if (TARGET_EFFICIENT_OVERLAPPING_UNALIGNED)
kono
parents:
diff changeset
914 {
kono
parents:
diff changeset
915 /* Move this load back so it doesn't go past the end.
kono
parents:
diff changeset
916 P8/P9 can do this efficiently. */
kono
parents:
diff changeset
917 unsigned int extra_bytes = load_mode_size - bytes_to_compare;
kono
parents:
diff changeset
918 cmp_bytes = bytes_to_compare;
kono
parents:
diff changeset
919 if (extra_bytes < offset)
kono
parents:
diff changeset
920 {
kono
parents:
diff changeset
921 offset -= extra_bytes;
kono
parents:
diff changeset
922 cmp_bytes = load_mode_size;
kono
parents:
diff changeset
923 bytes_to_compare = cmp_bytes;
kono
parents:
diff changeset
924 }
kono
parents:
diff changeset
925 }
kono
parents:
diff changeset
926 else
kono
parents:
diff changeset
927 /* P7 and earlier can't do the overlapping load trick fast,
kono
parents:
diff changeset
928 so this forces a non-overlapping load and a shift to get
kono
parents:
diff changeset
929 rid of the extra bytes. */
kono
parents:
diff changeset
930 cmp_bytes = bytes_to_compare;
kono
parents:
diff changeset
931
kono
parents:
diff changeset
932 src1 = adjust_address (orig_src1, load_mode, offset);
kono
parents:
diff changeset
933 src2 = adjust_address (orig_src2, load_mode, offset);
kono
parents:
diff changeset
934
kono
parents:
diff changeset
935 if (!REG_P (XEXP (src1, 0)))
kono
parents:
diff changeset
936 {
kono
parents:
diff changeset
937 rtx src1_reg = copy_addr_to_reg (XEXP (src1, 0));
kono
parents:
diff changeset
938 src1 = replace_equiv_address (src1, src1_reg);
kono
parents:
diff changeset
939 }
kono
parents:
diff changeset
940 set_mem_size (src1, cmp_bytes);
kono
parents:
diff changeset
941
kono
parents:
diff changeset
942 if (!REG_P (XEXP (src2, 0)))
kono
parents:
diff changeset
943 {
kono
parents:
diff changeset
944 rtx src2_reg = copy_addr_to_reg (XEXP (src2, 0));
kono
parents:
diff changeset
945 src2 = replace_equiv_address (src2, src2_reg);
kono
parents:
diff changeset
946 }
kono
parents:
diff changeset
947 set_mem_size (src2, cmp_bytes);
kono
parents:
diff changeset
948
kono
parents:
diff changeset
949 do_load_for_compare (tmp_reg_src1, src1, load_mode);
kono
parents:
diff changeset
950 do_load_for_compare (tmp_reg_src2, src2, load_mode);
kono
parents:
diff changeset
951
kono
parents:
diff changeset
952 /* We must always left-align the data we read, and
kono
parents:
diff changeset
953 clear any bytes to the right that are beyond the string.
kono
parents:
diff changeset
954 Otherwise the cmpb sequence won't produce the correct
kono
parents:
diff changeset
955 results. The beginning of the compare will be done
kono
parents:
diff changeset
956 with word_mode so will not have any extra shifts or
kono
parents:
diff changeset
957 clear rights. */
kono
parents:
diff changeset
958
kono
parents:
diff changeset
959 if (load_mode_size < word_mode_size)
kono
parents:
diff changeset
960 {
kono
parents:
diff changeset
961 /* Rotate left first. */
kono
parents:
diff changeset
962 rtx sh = GEN_INT (BITS_PER_UNIT * (word_mode_size - load_mode_size));
kono
parents:
diff changeset
963 if (word_mode == DImode)
kono
parents:
diff changeset
964 {
kono
parents:
diff changeset
965 emit_insn (gen_rotldi3 (tmp_reg_src1, tmp_reg_src1, sh));
kono
parents:
diff changeset
966 emit_insn (gen_rotldi3 (tmp_reg_src2, tmp_reg_src2, sh));
kono
parents:
diff changeset
967 }
kono
parents:
diff changeset
968 else
kono
parents:
diff changeset
969 {
kono
parents:
diff changeset
970 emit_insn (gen_rotlsi3 (tmp_reg_src1, tmp_reg_src1, sh));
kono
parents:
diff changeset
971 emit_insn (gen_rotlsi3 (tmp_reg_src2, tmp_reg_src2, sh));
kono
parents:
diff changeset
972 }
kono
parents:
diff changeset
973 }
kono
parents:
diff changeset
974
kono
parents:
diff changeset
975 if (cmp_bytes < word_mode_size)
kono
parents:
diff changeset
976 {
kono
parents:
diff changeset
977 /* Now clear right. This plus the rotate can be
kono
parents:
diff changeset
978 turned into a rldicr instruction. */
kono
parents:
diff changeset
979 HOST_WIDE_INT mb = BITS_PER_UNIT * (word_mode_size - cmp_bytes);
kono
parents:
diff changeset
980 rtx mask = GEN_INT (HOST_WIDE_INT_M1U << mb);
kono
parents:
diff changeset
981 if (word_mode == DImode)
kono
parents:
diff changeset
982 {
kono
parents:
diff changeset
983 emit_insn (gen_anddi3_mask (tmp_reg_src1, tmp_reg_src1, mask));
kono
parents:
diff changeset
984 emit_insn (gen_anddi3_mask (tmp_reg_src2, tmp_reg_src2, mask));
kono
parents:
diff changeset
985 }
kono
parents:
diff changeset
986 else
kono
parents:
diff changeset
987 {
kono
parents:
diff changeset
988 emit_insn (gen_andsi3_mask (tmp_reg_src1, tmp_reg_src1, mask));
kono
parents:
diff changeset
989 emit_insn (gen_andsi3_mask (tmp_reg_src2, tmp_reg_src2, mask));
kono
parents:
diff changeset
990 }
kono
parents:
diff changeset
991 }
kono
parents:
diff changeset
992
kono
parents:
diff changeset
993 /* Cases to handle. A and B are chunks of the two strings.
kono
parents:
diff changeset
994 1: Not end of comparison:
kono
parents:
diff changeset
995 A != B: branch to cleanup code to compute result.
kono
parents:
diff changeset
996 A == B: check for 0 byte, next block if not found.
kono
parents:
diff changeset
997 2: End of the inline comparison:
kono
parents:
diff changeset
998 A != B: branch to cleanup code to compute result.
kono
parents:
diff changeset
999 A == B: check for 0 byte, call strcmp/strncmp
kono
parents:
diff changeset
1000 3: compared requested N bytes:
kono
parents:
diff changeset
1001 A == B: branch to result 0.
kono
parents:
diff changeset
1002 A != B: cleanup code to compute result. */
kono
parents:
diff changeset
1003
kono
parents:
diff changeset
1004 unsigned HOST_WIDE_INT remain = bytes_to_compare - cmp_bytes;
kono
parents:
diff changeset
1005
kono
parents:
diff changeset
1006 rtx dst_label;
kono
parents:
diff changeset
1007 if (remain > 0 || equality_compare_rest)
kono
parents:
diff changeset
1008 {
kono
parents:
diff changeset
1009 /* Branch to cleanup code, otherwise fall through to do
kono
parents:
diff changeset
1010 more compares. */
kono
parents:
diff changeset
1011 if (!cleanup_label)
kono
parents:
diff changeset
1012 cleanup_label = gen_label_rtx ();
kono
parents:
diff changeset
1013 dst_label = cleanup_label;
kono
parents:
diff changeset
1014 }
kono
parents:
diff changeset
1015 else
kono
parents:
diff changeset
1016 /* Branch to end and produce result of 0. */
kono
parents:
diff changeset
1017 dst_label = final_move_label;
kono
parents:
diff changeset
1018
kono
parents:
diff changeset
1019 rtx lab_ref = gen_rtx_LABEL_REF (VOIDmode, dst_label);
kono
parents:
diff changeset
1020 rtx cond = gen_reg_rtx (CCmode);
kono
parents:
diff changeset
1021
kono
parents:
diff changeset
1022 /* Always produce the 0 result, it is needed if
kono
parents:
diff changeset
1023 cmpb finds a 0 byte in this chunk. */
kono
parents:
diff changeset
1024 rtx tmp = gen_rtx_MINUS (word_mode, tmp_reg_src1, tmp_reg_src2);
kono
parents:
diff changeset
1025 rs6000_emit_dot_insn (result_reg, tmp, 1, cond);
kono
parents:
diff changeset
1026
kono
parents:
diff changeset
1027 rtx cmp_rtx;
kono
parents:
diff changeset
1028 if (remain == 0 && !equality_compare_rest)
kono
parents:
diff changeset
1029 cmp_rtx = gen_rtx_EQ (VOIDmode, cond, const0_rtx);
kono
parents:
diff changeset
1030 else
kono
parents:
diff changeset
1031 cmp_rtx = gen_rtx_NE (VOIDmode, cond, const0_rtx);
kono
parents:
diff changeset
1032
kono
parents:
diff changeset
1033 rtx ifelse = gen_rtx_IF_THEN_ELSE (VOIDmode, cmp_rtx,
kono
parents:
diff changeset
1034 lab_ref, pc_rtx);
kono
parents:
diff changeset
1035 rtx j = emit_jump_insn (gen_rtx_SET (pc_rtx, ifelse));
kono
parents:
diff changeset
1036 JUMP_LABEL (j) = dst_label;
kono
parents:
diff changeset
1037 LABEL_NUSES (dst_label) += 1;
kono
parents:
diff changeset
1038
kono
parents:
diff changeset
1039 if (remain > 0 || equality_compare_rest)
kono
parents:
diff changeset
1040 {
kono
parents:
diff changeset
1041 /* Generate a cmpb to test for a 0 byte and branch
kono
parents:
diff changeset
1042 to final result if found. */
kono
parents:
diff changeset
1043 rtx cmpb_zero = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1044 rtx lab_ref_fin = gen_rtx_LABEL_REF (VOIDmode, final_move_label);
kono
parents:
diff changeset
1045 rtx condz = gen_reg_rtx (CCmode);
kono
parents:
diff changeset
1046 rtx zero_reg = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1047 if (word_mode == SImode)
kono
parents:
diff changeset
1048 {
kono
parents:
diff changeset
1049 emit_insn (gen_movsi (zero_reg, GEN_INT (0)));
kono
parents:
diff changeset
1050 emit_insn (gen_cmpbsi3 (cmpb_zero, tmp_reg_src1, zero_reg));
kono
parents:
diff changeset
1051 if (cmp_bytes < word_mode_size)
kono
parents:
diff changeset
1052 {
kono
parents:
diff changeset
1053 /* Don't want to look at zero bytes past end. */
kono
parents:
diff changeset
1054 HOST_WIDE_INT mb =
kono
parents:
diff changeset
1055 BITS_PER_UNIT * (word_mode_size - cmp_bytes);
kono
parents:
diff changeset
1056 rtx mask = GEN_INT (HOST_WIDE_INT_M1U << mb);
kono
parents:
diff changeset
1057 emit_insn (gen_andsi3_mask (cmpb_zero, cmpb_zero, mask));
kono
parents:
diff changeset
1058 }
kono
parents:
diff changeset
1059 }
kono
parents:
diff changeset
1060 else
kono
parents:
diff changeset
1061 {
kono
parents:
diff changeset
1062 emit_insn (gen_movdi (zero_reg, GEN_INT (0)));
kono
parents:
diff changeset
1063 emit_insn (gen_cmpbdi3 (cmpb_zero, tmp_reg_src1, zero_reg));
kono
parents:
diff changeset
1064 if (cmp_bytes < word_mode_size)
kono
parents:
diff changeset
1065 {
kono
parents:
diff changeset
1066 /* Don't want to look at zero bytes past end. */
kono
parents:
diff changeset
1067 HOST_WIDE_INT mb =
kono
parents:
diff changeset
1068 BITS_PER_UNIT * (word_mode_size - cmp_bytes);
kono
parents:
diff changeset
1069 rtx mask = GEN_INT (HOST_WIDE_INT_M1U << mb);
kono
parents:
diff changeset
1070 emit_insn (gen_anddi3_mask (cmpb_zero, cmpb_zero, mask));
kono
parents:
diff changeset
1071 }
kono
parents:
diff changeset
1072 }
kono
parents:
diff changeset
1073
kono
parents:
diff changeset
1074 emit_move_insn (condz, gen_rtx_COMPARE (CCmode, cmpb_zero, zero_reg));
kono
parents:
diff changeset
1075 rtx cmpnz_rtx = gen_rtx_NE (VOIDmode, condz, const0_rtx);
kono
parents:
diff changeset
1076 rtx ifelse = gen_rtx_IF_THEN_ELSE (VOIDmode, cmpnz_rtx,
kono
parents:
diff changeset
1077 lab_ref_fin, pc_rtx);
kono
parents:
diff changeset
1078 rtx j2 = emit_jump_insn (gen_rtx_SET (pc_rtx, ifelse));
kono
parents:
diff changeset
1079 JUMP_LABEL (j2) = final_move_label;
kono
parents:
diff changeset
1080 LABEL_NUSES (final_move_label) += 1;
kono
parents:
diff changeset
1081
kono
parents:
diff changeset
1082 }
kono
parents:
diff changeset
1083
kono
parents:
diff changeset
1084 offset += cmp_bytes;
kono
parents:
diff changeset
1085 bytes_to_compare -= cmp_bytes;
kono
parents:
diff changeset
1086 }
kono
parents:
diff changeset
1087
kono
parents:
diff changeset
1088 if (equality_compare_rest)
kono
parents:
diff changeset
1089 {
kono
parents:
diff changeset
1090 /* Update pointers past what has been compared already. */
kono
parents:
diff changeset
1091 src1 = adjust_address (orig_src1, load_mode, offset);
kono
parents:
diff changeset
1092 src2 = adjust_address (orig_src2, load_mode, offset);
kono
parents:
diff changeset
1093
kono
parents:
diff changeset
1094 if (!REG_P (XEXP (src1, 0)))
kono
parents:
diff changeset
1095 {
kono
parents:
diff changeset
1096 rtx src1_reg = copy_addr_to_reg (XEXP (src1, 0));
kono
parents:
diff changeset
1097 src1 = replace_equiv_address (src1, src1_reg);
kono
parents:
diff changeset
1098 }
kono
parents:
diff changeset
1099 set_mem_size (src1, cmp_bytes);
kono
parents:
diff changeset
1100
kono
parents:
diff changeset
1101 if (!REG_P (XEXP (src2, 0)))
kono
parents:
diff changeset
1102 {
kono
parents:
diff changeset
1103 rtx src2_reg = copy_addr_to_reg (XEXP (src2, 0));
kono
parents:
diff changeset
1104 src2 = replace_equiv_address (src2, src2_reg);
kono
parents:
diff changeset
1105 }
kono
parents:
diff changeset
1106 set_mem_size (src2, cmp_bytes);
kono
parents:
diff changeset
1107
kono
parents:
diff changeset
1108 /* Construct call to strcmp/strncmp to compare the rest of the string. */
kono
parents:
diff changeset
1109 if (no_length)
kono
parents:
diff changeset
1110 {
kono
parents:
diff changeset
1111 tree fun = builtin_decl_explicit (BUILT_IN_STRCMP);
kono
parents:
diff changeset
1112 emit_library_call_value (XEXP (DECL_RTL (fun), 0),
kono
parents:
diff changeset
1113 target, LCT_NORMAL, GET_MODE (target),
kono
parents:
diff changeset
1114 force_reg (Pmode, XEXP (src1, 0)), Pmode,
kono
parents:
diff changeset
1115 force_reg (Pmode, XEXP (src2, 0)), Pmode);
kono
parents:
diff changeset
1116 }
kono
parents:
diff changeset
1117 else
kono
parents:
diff changeset
1118 {
kono
parents:
diff changeset
1119 rtx len_rtx;
kono
parents:
diff changeset
1120 if (TARGET_64BIT)
kono
parents:
diff changeset
1121 len_rtx = gen_reg_rtx (DImode);
kono
parents:
diff changeset
1122 else
kono
parents:
diff changeset
1123 len_rtx = gen_reg_rtx (SImode);
kono
parents:
diff changeset
1124
kono
parents:
diff changeset
1125 emit_move_insn (len_rtx, GEN_INT (bytes - compare_length));
kono
parents:
diff changeset
1126 tree fun = builtin_decl_explicit (BUILT_IN_STRNCMP);
kono
parents:
diff changeset
1127 emit_library_call_value (XEXP (DECL_RTL (fun), 0),
kono
parents:
diff changeset
1128 target, LCT_NORMAL, GET_MODE (target),
kono
parents:
diff changeset
1129 force_reg (Pmode, XEXP (src1, 0)), Pmode,
kono
parents:
diff changeset
1130 force_reg (Pmode, XEXP (src2, 0)), Pmode,
kono
parents:
diff changeset
1131 len_rtx, GET_MODE (len_rtx));
kono
parents:
diff changeset
1132 }
kono
parents:
diff changeset
1133
kono
parents:
diff changeset
1134 rtx fin_ref = gen_rtx_LABEL_REF (VOIDmode, final_label);
kono
parents:
diff changeset
1135 rtx jmp = emit_jump_insn (gen_rtx_SET (pc_rtx, fin_ref));
kono
parents:
diff changeset
1136 JUMP_LABEL (jmp) = final_label;
kono
parents:
diff changeset
1137 LABEL_NUSES (final_label) += 1;
kono
parents:
diff changeset
1138 emit_barrier ();
kono
parents:
diff changeset
1139 }
kono
parents:
diff changeset
1140
kono
parents:
diff changeset
1141 if (cleanup_label)
kono
parents:
diff changeset
1142 emit_label (cleanup_label);
kono
parents:
diff changeset
1143
kono
parents:
diff changeset
1144 /* Generate the final sequence that identifies the differing
kono
parents:
diff changeset
1145 byte and generates the final result, taking into account
kono
parents:
diff changeset
1146 zero bytes:
kono
parents:
diff changeset
1147
kono
parents:
diff changeset
1148 cmpb cmpb_result1, src1, src2
kono
parents:
diff changeset
1149 cmpb cmpb_result2, src1, zero
kono
parents:
diff changeset
1150 orc cmpb_result1, cmp_result1, cmpb_result2
kono
parents:
diff changeset
1151 cntlzd get bit of first zero/diff byte
kono
parents:
diff changeset
1152 addi convert for rldcl use
kono
parents:
diff changeset
1153 rldcl rldcl extract diff/zero byte
kono
parents:
diff changeset
1154 subf subtract for final result
kono
parents:
diff changeset
1155 */
kono
parents:
diff changeset
1156
kono
parents:
diff changeset
1157 rtx cmpb_diff = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1158 rtx cmpb_zero = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1159 rtx rot_amt = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1160 rtx zero_reg = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1161
kono
parents:
diff changeset
1162 rtx rot1_1 = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1163 rtx rot1_2 = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1164 rtx rot2_1 = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1165 rtx rot2_2 = gen_reg_rtx (word_mode);
kono
parents:
diff changeset
1166
kono
parents:
diff changeset
1167 if (word_mode == SImode)
kono
parents:
diff changeset
1168 {
kono
parents:
diff changeset
1169 emit_insn (gen_cmpbsi3 (cmpb_diff, tmp_reg_src1, tmp_reg_src2));
kono
parents:
diff changeset
1170 emit_insn (gen_movsi (zero_reg, GEN_INT (0)));
kono
parents:
diff changeset
1171 emit_insn (gen_cmpbsi3 (cmpb_zero, tmp_reg_src1, zero_reg));
kono
parents:
diff changeset
1172 emit_insn (gen_one_cmplsi2 (cmpb_diff,cmpb_diff));
kono
parents:
diff changeset
1173 emit_insn (gen_iorsi3 (cmpb_diff, cmpb_diff, cmpb_zero));
kono
parents:
diff changeset
1174 emit_insn (gen_clzsi2 (rot_amt, cmpb_diff));
kono
parents:
diff changeset
1175 emit_insn (gen_addsi3 (rot_amt, rot_amt, GEN_INT (8)));
kono
parents:
diff changeset
1176 emit_insn (gen_rotlsi3 (rot1_1, tmp_reg_src1,
kono
parents:
diff changeset
1177 gen_lowpart (SImode, rot_amt)));
kono
parents:
diff changeset
1178 emit_insn (gen_andsi3_mask (rot1_2, rot1_1, GEN_INT (0xff)));
kono
parents:
diff changeset
1179 emit_insn (gen_rotlsi3 (rot2_1, tmp_reg_src2,
kono
parents:
diff changeset
1180 gen_lowpart (SImode, rot_amt)));
kono
parents:
diff changeset
1181 emit_insn (gen_andsi3_mask (rot2_2, rot2_1, GEN_INT (0xff)));
kono
parents:
diff changeset
1182 emit_insn (gen_subsi3 (result_reg, rot1_2, rot2_2));
kono
parents:
diff changeset
1183 }
kono
parents:
diff changeset
1184 else
kono
parents:
diff changeset
1185 {
kono
parents:
diff changeset
1186 emit_insn (gen_cmpbdi3 (cmpb_diff, tmp_reg_src1, tmp_reg_src2));
kono
parents:
diff changeset
1187 emit_insn (gen_movdi (zero_reg, GEN_INT (0)));
kono
parents:
diff changeset
1188 emit_insn (gen_cmpbdi3 (cmpb_zero, tmp_reg_src1, zero_reg));
kono
parents:
diff changeset
1189 emit_insn (gen_one_cmpldi2 (cmpb_diff,cmpb_diff));
kono
parents:
diff changeset
1190 emit_insn (gen_iordi3 (cmpb_diff, cmpb_diff, cmpb_zero));
kono
parents:
diff changeset
1191 emit_insn (gen_clzdi2 (rot_amt, cmpb_diff));
kono
parents:
diff changeset
1192 emit_insn (gen_adddi3 (rot_amt, rot_amt, GEN_INT (8)));
kono
parents:
diff changeset
1193 emit_insn (gen_rotldi3 (rot1_1, tmp_reg_src1,
kono
parents:
diff changeset
1194 gen_lowpart (SImode, rot_amt)));
kono
parents:
diff changeset
1195 emit_insn (gen_anddi3_mask (rot1_2, rot1_1, GEN_INT (0xff)));
kono
parents:
diff changeset
1196 emit_insn (gen_rotldi3 (rot2_1, tmp_reg_src2,
kono
parents:
diff changeset
1197 gen_lowpart (SImode, rot_amt)));
kono
parents:
diff changeset
1198 emit_insn (gen_anddi3_mask (rot2_2, rot2_1, GEN_INT (0xff)));
kono
parents:
diff changeset
1199 emit_insn (gen_subdi3 (result_reg, rot1_2, rot2_2));
kono
parents:
diff changeset
1200 }
kono
parents:
diff changeset
1201
kono
parents:
diff changeset
1202 emit_label (final_move_label);
kono
parents:
diff changeset
1203 emit_insn (gen_movsi (target,
kono
parents:
diff changeset
1204 gen_lowpart (SImode, result_reg)));
kono
parents:
diff changeset
1205 emit_label (final_label);
kono
parents:
diff changeset
1206 return true;
kono
parents:
diff changeset
1207 }
kono
parents:
diff changeset
1208
kono
parents:
diff changeset
1209 /* Expand a block move operation, and return 1 if successful. Return 0
kono
parents:
diff changeset
1210 if we should let the compiler generate normal code.
kono
parents:
diff changeset
1211
kono
parents:
diff changeset
1212 operands[0] is the destination
kono
parents:
diff changeset
1213 operands[1] is the source
kono
parents:
diff changeset
1214 operands[2] is the length
kono
parents:
diff changeset
1215 operands[3] is the alignment */
kono
parents:
diff changeset
1216
kono
parents:
diff changeset
1217 #define MAX_MOVE_REG 4
kono
parents:
diff changeset
1218
kono
parents:
diff changeset
1219 int
kono
parents:
diff changeset
1220 expand_block_move (rtx operands[])
kono
parents:
diff changeset
1221 {
kono
parents:
diff changeset
1222 rtx orig_dest = operands[0];
kono
parents:
diff changeset
1223 rtx orig_src = operands[1];
kono
parents:
diff changeset
1224 rtx bytes_rtx = operands[2];
kono
parents:
diff changeset
1225 rtx align_rtx = operands[3];
kono
parents:
diff changeset
1226 int constp = (GET_CODE (bytes_rtx) == CONST_INT);
kono
parents:
diff changeset
1227 int align;
kono
parents:
diff changeset
1228 int bytes;
kono
parents:
diff changeset
1229 int offset;
kono
parents:
diff changeset
1230 int move_bytes;
kono
parents:
diff changeset
1231 rtx stores[MAX_MOVE_REG];
kono
parents:
diff changeset
1232 int num_reg = 0;
kono
parents:
diff changeset
1233
kono
parents:
diff changeset
1234 /* If this is not a fixed size move, just call memcpy */
kono
parents:
diff changeset
1235 if (! constp)
kono
parents:
diff changeset
1236 return 0;
kono
parents:
diff changeset
1237
kono
parents:
diff changeset
1238 /* This must be a fixed size alignment */
kono
parents:
diff changeset
1239 gcc_assert (GET_CODE (align_rtx) == CONST_INT);
kono
parents:
diff changeset
1240 align = INTVAL (align_rtx) * BITS_PER_UNIT;
kono
parents:
diff changeset
1241
kono
parents:
diff changeset
1242 /* Anything to move? */
kono
parents:
diff changeset
1243 bytes = INTVAL (bytes_rtx);
kono
parents:
diff changeset
1244 if (bytes <= 0)
kono
parents:
diff changeset
1245 return 1;
kono
parents:
diff changeset
1246
kono
parents:
diff changeset
1247 if (bytes > rs6000_block_move_inline_limit)
kono
parents:
diff changeset
1248 return 0;
kono
parents:
diff changeset
1249
kono
parents:
diff changeset
1250 for (offset = 0; bytes > 0; offset += move_bytes, bytes -= move_bytes)
kono
parents:
diff changeset
1251 {
kono
parents:
diff changeset
1252 union {
kono
parents:
diff changeset
1253 rtx (*movmemsi) (rtx, rtx, rtx, rtx);
kono
parents:
diff changeset
1254 rtx (*mov) (rtx, rtx);
kono
parents:
diff changeset
1255 } gen_func;
kono
parents:
diff changeset
1256 machine_mode mode = BLKmode;
kono
parents:
diff changeset
1257 rtx src, dest;
kono
parents:
diff changeset
1258
kono
parents:
diff changeset
1259 /* Altivec first, since it will be faster than a string move
kono
parents:
diff changeset
1260 when it applies, and usually not significantly larger. */
kono
parents:
diff changeset
1261 if (TARGET_ALTIVEC && bytes >= 16 && align >= 128)
kono
parents:
diff changeset
1262 {
kono
parents:
diff changeset
1263 move_bytes = 16;
kono
parents:
diff changeset
1264 mode = V4SImode;
kono
parents:
diff changeset
1265 gen_func.mov = gen_movv4si;
kono
parents:
diff changeset
1266 }
kono
parents:
diff changeset
1267 else if (TARGET_STRING
kono
parents:
diff changeset
1268 && bytes > 24 /* move up to 32 bytes at a time */
kono
parents:
diff changeset
1269 && ! fixed_regs[5]
kono
parents:
diff changeset
1270 && ! fixed_regs[6]
kono
parents:
diff changeset
1271 && ! fixed_regs[7]
kono
parents:
diff changeset
1272 && ! fixed_regs[8]
kono
parents:
diff changeset
1273 && ! fixed_regs[9]
kono
parents:
diff changeset
1274 && ! fixed_regs[10]
kono
parents:
diff changeset
1275 && ! fixed_regs[11]
kono
parents:
diff changeset
1276 && ! fixed_regs[12])
kono
parents:
diff changeset
1277 {
kono
parents:
diff changeset
1278 move_bytes = (bytes > 32) ? 32 : bytes;
kono
parents:
diff changeset
1279 gen_func.movmemsi = gen_movmemsi_8reg;
kono
parents:
diff changeset
1280 }
kono
parents:
diff changeset
1281 else if (TARGET_STRING
kono
parents:
diff changeset
1282 && bytes > 16 /* move up to 24 bytes at a time */
kono
parents:
diff changeset
1283 && ! fixed_regs[5]
kono
parents:
diff changeset
1284 && ! fixed_regs[6]
kono
parents:
diff changeset
1285 && ! fixed_regs[7]
kono
parents:
diff changeset
1286 && ! fixed_regs[8]
kono
parents:
diff changeset
1287 && ! fixed_regs[9]
kono
parents:
diff changeset
1288 && ! fixed_regs[10])
kono
parents:
diff changeset
1289 {
kono
parents:
diff changeset
1290 move_bytes = (bytes > 24) ? 24 : bytes;
kono
parents:
diff changeset
1291 gen_func.movmemsi = gen_movmemsi_6reg;
kono
parents:
diff changeset
1292 }
kono
parents:
diff changeset
1293 else if (TARGET_STRING
kono
parents:
diff changeset
1294 && bytes > 8 /* move up to 16 bytes at a time */
kono
parents:
diff changeset
1295 && ! fixed_regs[5]
kono
parents:
diff changeset
1296 && ! fixed_regs[6]
kono
parents:
diff changeset
1297 && ! fixed_regs[7]
kono
parents:
diff changeset
1298 && ! fixed_regs[8])
kono
parents:
diff changeset
1299 {
kono
parents:
diff changeset
1300 move_bytes = (bytes > 16) ? 16 : bytes;
kono
parents:
diff changeset
1301 gen_func.movmemsi = gen_movmemsi_4reg;
kono
parents:
diff changeset
1302 }
kono
parents:
diff changeset
1303 else if (bytes >= 8 && TARGET_POWERPC64
kono
parents:
diff changeset
1304 && (align >= 64 || !STRICT_ALIGNMENT))
kono
parents:
diff changeset
1305 {
kono
parents:
diff changeset
1306 move_bytes = 8;
kono
parents:
diff changeset
1307 mode = DImode;
kono
parents:
diff changeset
1308 gen_func.mov = gen_movdi;
kono
parents:
diff changeset
1309 if (offset == 0 && align < 64)
kono
parents:
diff changeset
1310 {
kono
parents:
diff changeset
1311 rtx addr;
kono
parents:
diff changeset
1312
kono
parents:
diff changeset
1313 /* If the address form is reg+offset with offset not a
kono
parents:
diff changeset
1314 multiple of four, reload into reg indirect form here
kono
parents:
diff changeset
1315 rather than waiting for reload. This way we get one
kono
parents:
diff changeset
1316 reload, not one per load and/or store. */
kono
parents:
diff changeset
1317 addr = XEXP (orig_dest, 0);
kono
parents:
diff changeset
1318 if ((GET_CODE (addr) == PLUS || GET_CODE (addr) == LO_SUM)
kono
parents:
diff changeset
1319 && GET_CODE (XEXP (addr, 1)) == CONST_INT
kono
parents:
diff changeset
1320 && (INTVAL (XEXP (addr, 1)) & 3) != 0)
kono
parents:
diff changeset
1321 {
kono
parents:
diff changeset
1322 addr = copy_addr_to_reg (addr);
kono
parents:
diff changeset
1323 orig_dest = replace_equiv_address (orig_dest, addr);
kono
parents:
diff changeset
1324 }
kono
parents:
diff changeset
1325 addr = XEXP (orig_src, 0);
kono
parents:
diff changeset
1326 if ((GET_CODE (addr) == PLUS || GET_CODE (addr) == LO_SUM)
kono
parents:
diff changeset
1327 && GET_CODE (XEXP (addr, 1)) == CONST_INT
kono
parents:
diff changeset
1328 && (INTVAL (XEXP (addr, 1)) & 3) != 0)
kono
parents:
diff changeset
1329 {
kono
parents:
diff changeset
1330 addr = copy_addr_to_reg (addr);
kono
parents:
diff changeset
1331 orig_src = replace_equiv_address (orig_src, addr);
kono
parents:
diff changeset
1332 }
kono
parents:
diff changeset
1333 }
kono
parents:
diff changeset
1334 }
kono
parents:
diff changeset
1335 else if (TARGET_STRING && bytes > 4 && !TARGET_POWERPC64)
kono
parents:
diff changeset
1336 { /* move up to 8 bytes at a time */
kono
parents:
diff changeset
1337 move_bytes = (bytes > 8) ? 8 : bytes;
kono
parents:
diff changeset
1338 gen_func.movmemsi = gen_movmemsi_2reg;
kono
parents:
diff changeset
1339 }
kono
parents:
diff changeset
1340 else if (bytes >= 4 && (align >= 32 || !STRICT_ALIGNMENT))
kono
parents:
diff changeset
1341 { /* move 4 bytes */
kono
parents:
diff changeset
1342 move_bytes = 4;
kono
parents:
diff changeset
1343 mode = SImode;
kono
parents:
diff changeset
1344 gen_func.mov = gen_movsi;
kono
parents:
diff changeset
1345 }
kono
parents:
diff changeset
1346 else if (bytes >= 2 && (align >= 16 || !STRICT_ALIGNMENT))
kono
parents:
diff changeset
1347 { /* move 2 bytes */
kono
parents:
diff changeset
1348 move_bytes = 2;
kono
parents:
diff changeset
1349 mode = HImode;
kono
parents:
diff changeset
1350 gen_func.mov = gen_movhi;
kono
parents:
diff changeset
1351 }
kono
parents:
diff changeset
1352 else if (TARGET_STRING && bytes > 1)
kono
parents:
diff changeset
1353 { /* move up to 4 bytes at a time */
kono
parents:
diff changeset
1354 move_bytes = (bytes > 4) ? 4 : bytes;
kono
parents:
diff changeset
1355 gen_func.movmemsi = gen_movmemsi_1reg;
kono
parents:
diff changeset
1356 }
kono
parents:
diff changeset
1357 else /* move 1 byte at a time */
kono
parents:
diff changeset
1358 {
kono
parents:
diff changeset
1359 move_bytes = 1;
kono
parents:
diff changeset
1360 mode = QImode;
kono
parents:
diff changeset
1361 gen_func.mov = gen_movqi;
kono
parents:
diff changeset
1362 }
kono
parents:
diff changeset
1363
kono
parents:
diff changeset
1364 src = adjust_address (orig_src, mode, offset);
kono
parents:
diff changeset
1365 dest = adjust_address (orig_dest, mode, offset);
kono
parents:
diff changeset
1366
kono
parents:
diff changeset
1367 if (mode != BLKmode)
kono
parents:
diff changeset
1368 {
kono
parents:
diff changeset
1369 rtx tmp_reg = gen_reg_rtx (mode);
kono
parents:
diff changeset
1370
kono
parents:
diff changeset
1371 emit_insn ((*gen_func.mov) (tmp_reg, src));
kono
parents:
diff changeset
1372 stores[num_reg++] = (*gen_func.mov) (dest, tmp_reg);
kono
parents:
diff changeset
1373 }
kono
parents:
diff changeset
1374
kono
parents:
diff changeset
1375 if (mode == BLKmode || num_reg >= MAX_MOVE_REG || bytes == move_bytes)
kono
parents:
diff changeset
1376 {
kono
parents:
diff changeset
1377 int i;
kono
parents:
diff changeset
1378 for (i = 0; i < num_reg; i++)
kono
parents:
diff changeset
1379 emit_insn (stores[i]);
kono
parents:
diff changeset
1380 num_reg = 0;
kono
parents:
diff changeset
1381 }
kono
parents:
diff changeset
1382
kono
parents:
diff changeset
1383 if (mode == BLKmode)
kono
parents:
diff changeset
1384 {
kono
parents:
diff changeset
1385 /* Move the address into scratch registers. The movmemsi
kono
parents:
diff changeset
1386 patterns require zero offset. */
kono
parents:
diff changeset
1387 if (!REG_P (XEXP (src, 0)))
kono
parents:
diff changeset
1388 {
kono
parents:
diff changeset
1389 rtx src_reg = copy_addr_to_reg (XEXP (src, 0));
kono
parents:
diff changeset
1390 src = replace_equiv_address (src, src_reg);
kono
parents:
diff changeset
1391 }
kono
parents:
diff changeset
1392 set_mem_size (src, move_bytes);
kono
parents:
diff changeset
1393
kono
parents:
diff changeset
1394 if (!REG_P (XEXP (dest, 0)))
kono
parents:
diff changeset
1395 {
kono
parents:
diff changeset
1396 rtx dest_reg = copy_addr_to_reg (XEXP (dest, 0));
kono
parents:
diff changeset
1397 dest = replace_equiv_address (dest, dest_reg);
kono
parents:
diff changeset
1398 }
kono
parents:
diff changeset
1399 set_mem_size (dest, move_bytes);
kono
parents:
diff changeset
1400
kono
parents:
diff changeset
1401 emit_insn ((*gen_func.movmemsi) (dest, src,
kono
parents:
diff changeset
1402 GEN_INT (move_bytes & 31),
kono
parents:
diff changeset
1403 align_rtx));
kono
parents:
diff changeset
1404 }
kono
parents:
diff changeset
1405 }
kono
parents:
diff changeset
1406
kono
parents:
diff changeset
1407 return 1;
kono
parents:
diff changeset
1408 }
kono
parents:
diff changeset
1409
kono
parents:
diff changeset
1410
kono
parents:
diff changeset
1411 /* Return a string to perform a load_multiple operation.
kono
parents:
diff changeset
1412 operands[0] is the vector.
kono
parents:
diff changeset
1413 operands[1] is the source address.
kono
parents:
diff changeset
1414 operands[2] is the first destination register. */
kono
parents:
diff changeset
1415
kono
parents:
diff changeset
1416 const char *
kono
parents:
diff changeset
1417 rs6000_output_load_multiple (rtx operands[3])
kono
parents:
diff changeset
1418 {
kono
parents:
diff changeset
1419 /* We have to handle the case where the pseudo used to contain the address
kono
parents:
diff changeset
1420 is assigned to one of the output registers. */
kono
parents:
diff changeset
1421 int i, j;
kono
parents:
diff changeset
1422 int words = XVECLEN (operands[0], 0);
kono
parents:
diff changeset
1423 rtx xop[10];
kono
parents:
diff changeset
1424
kono
parents:
diff changeset
1425 if (XVECLEN (operands[0], 0) == 1)
kono
parents:
diff changeset
1426 return "lwz %2,0(%1)";
kono
parents:
diff changeset
1427
kono
parents:
diff changeset
1428 for (i = 0; i < words; i++)
kono
parents:
diff changeset
1429 if (refers_to_regno_p (REGNO (operands[2]) + i, operands[1]))
kono
parents:
diff changeset
1430 {
kono
parents:
diff changeset
1431 if (i == words-1)
kono
parents:
diff changeset
1432 {
kono
parents:
diff changeset
1433 xop[0] = GEN_INT (4 * (words-1));
kono
parents:
diff changeset
1434 xop[1] = operands[1];
kono
parents:
diff changeset
1435 xop[2] = operands[2];
kono
parents:
diff changeset
1436 output_asm_insn ("lswi %2,%1,%0\n\tlwz %1,%0(%1)", xop);
kono
parents:
diff changeset
1437 return "";
kono
parents:
diff changeset
1438 }
kono
parents:
diff changeset
1439 else if (i == 0)
kono
parents:
diff changeset
1440 {
kono
parents:
diff changeset
1441 xop[0] = GEN_INT (4 * (words-1));
kono
parents:
diff changeset
1442 xop[1] = operands[1];
kono
parents:
diff changeset
1443 xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + 1);
kono
parents:
diff changeset
1444 output_asm_insn ("addi %1,%1,4\n\tlswi %2,%1,%0\n\tlwz %1,-4(%1)", xop);
kono
parents:
diff changeset
1445 return "";
kono
parents:
diff changeset
1446 }
kono
parents:
diff changeset
1447 else
kono
parents:
diff changeset
1448 {
kono
parents:
diff changeset
1449 for (j = 0; j < words; j++)
kono
parents:
diff changeset
1450 if (j != i)
kono
parents:
diff changeset
1451 {
kono
parents:
diff changeset
1452 xop[0] = GEN_INT (j * 4);
kono
parents:
diff changeset
1453 xop[1] = operands[1];
kono
parents:
diff changeset
1454 xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + j);
kono
parents:
diff changeset
1455 output_asm_insn ("lwz %2,%0(%1)", xop);
kono
parents:
diff changeset
1456 }
kono
parents:
diff changeset
1457 xop[0] = GEN_INT (i * 4);
kono
parents:
diff changeset
1458 xop[1] = operands[1];
kono
parents:
diff changeset
1459 output_asm_insn ("lwz %1,%0(%1)", xop);
kono
parents:
diff changeset
1460 return "";
kono
parents:
diff changeset
1461 }
kono
parents:
diff changeset
1462 }
kono
parents:
diff changeset
1463
kono
parents:
diff changeset
1464 return "lswi %2,%1,%N0";
kono
parents:
diff changeset
1465 }
kono
parents:
diff changeset
1466