forked from illumos/gcc
/
stmt.c
3281 lines (2770 loc) · 96.4 KB
/
stmt.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Expands front end tree to back end RTL for GCC
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* This file handles the generation of rtl code from tree structure
above the level of expressions, using subroutines in exp*.c and emit-rtl.c.
The functions whose names start with `expand_' are called by the
expander to generate RTL instructions for various kinds of constructs. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "rtl.h"
#include "hard-reg-set.h"
#include "tree.h"
#include "tm_p.h"
#include "flags.h"
#include "except.h"
#include "function.h"
#include "insn-config.h"
#include "expr.h"
#include "libfuncs.h"
#include "recog.h"
#include "machmode.h"
#include "diagnostic-core.h"
#include "output.h"
#include "ggc.h"
#include "langhooks.h"
#include "predict.h"
#include "optabs.h"
#include "target.h"
#include "gimple.h"
#include "regs.h"
#include "alloc-pool.h"
#include "pretty-print.h"
#include "bitmap.h"
/* Functions and data structures for expanding case statements. */
/* Case label structure, used to hold info on labels within case
statements. We handle "range" labels; for a single-value label
as in C, the high and low limits are the same.
We start with a vector of case nodes sorted in ascending order, and
the default label as the last element in the vector. Before expanding
to RTL, we transform this vector into a list linked via the RIGHT
fields in the case_node struct. Nodes with higher case values are
later in the list.
Switch statements can be output in three forms. A branch table is
used if there are more than a few labels and the labels are dense
within the range between the smallest and largest case value. If a
branch table is used, no further manipulations are done with the case
node chain.
The alternative to the use of a branch table is to generate a series
of compare and jump insns. When that is done, we use the LEFT, RIGHT,
and PARENT fields to hold a binary tree. Initially the tree is
totally unbalanced, with everything on the right. We balance the tree
with nodes on the left having lower case values than the parent
and nodes on the right having higher values. We then output the tree
in order.
For very small, suitable switch statements, we can generate a series
of simple bit test and branches instead. */
struct case_node
{
struct case_node *left; /* Left son in binary tree */
struct case_node *right; /* Right son in binary tree; also node chain */
struct case_node *parent; /* Parent of node in binary tree */
tree low; /* Lowest index value for this label */
tree high; /* Highest index value for this label */
tree code_label; /* Label to jump to when node matches */
};
typedef struct case_node case_node;
typedef struct case_node *case_node_ptr;
/* These are used by estimate_case_costs and balance_case_nodes. */
/* This must be a signed type, and non-ANSI compilers lack signed char. */
static short cost_table_[129];
static int use_cost_table;
static int cost_table_initialized;
/* Special care is needed because we allow -1, but TREE_INT_CST_LOW
is unsigned. */
#define COST_TABLE(I) cost_table_[(unsigned HOST_WIDE_INT) ((I) + 1)]
static int n_occurrences (int, const char *);
static bool tree_conflicts_with_clobbers_p (tree, HARD_REG_SET *);
static void expand_nl_goto_receiver (void);
static bool check_operand_nalternatives (tree, tree);
static bool check_unique_operand_names (tree, tree, tree);
static char *resolve_operand_name_1 (char *, tree, tree, tree);
static void expand_null_return_1 (void);
static void expand_value_return (rtx);
static int estimate_case_costs (case_node_ptr);
static bool lshift_cheap_p (void);
static int case_bit_test_cmp (const void *, const void *);
static void emit_case_bit_tests (tree, tree, tree, tree, case_node_ptr, rtx);
static void balance_case_nodes (case_node_ptr *, case_node_ptr);
static int node_has_low_bound (case_node_ptr, tree);
static int node_has_high_bound (case_node_ptr, tree);
static int node_is_bounded (case_node_ptr, tree);
static void emit_case_nodes (rtx, case_node_ptr, rtx, tree);
static struct case_node *add_case_node (struct case_node *, tree,
tree, tree, tree, alloc_pool);
/* Return the rtx-label that corresponds to a LABEL_DECL,
creating it if necessary. */
rtx
label_rtx (tree label)
{
gcc_assert (TREE_CODE (label) == LABEL_DECL);
if (!DECL_RTL_SET_P (label))
{
rtx r = gen_label_rtx ();
SET_DECL_RTL (label, r);
if (FORCED_LABEL (label) || DECL_NONLOCAL (label))
LABEL_PRESERVE_P (r) = 1;
}
return DECL_RTL (label);
}
/* As above, but also put it on the forced-reference list of the
function that contains it. */
rtx
force_label_rtx (tree label)
{
rtx ref = label_rtx (label);
tree function = decl_function_context (label);
gcc_assert (function);
forced_labels = gen_rtx_EXPR_LIST (VOIDmode, ref, forced_labels);
return ref;
}
/* Add an unconditional jump to LABEL as the next sequential instruction. */
void
emit_jump (rtx label)
{
do_pending_stack_adjust ();
emit_jump_insn (gen_jump (label));
emit_barrier ();
}
/* Emit code to jump to the address
specified by the pointer expression EXP. */
void
expand_computed_goto (tree exp)
{
rtx x = expand_normal (exp);
x = convert_memory_address (Pmode, x);
do_pending_stack_adjust ();
emit_indirect_jump (x);
}
/* Handle goto statements and the labels that they can go to. */
/* Specify the location in the RTL code of a label LABEL,
which is a LABEL_DECL tree node.
This is used for the kind of label that the user can jump to with a
goto statement, and for alternatives of a switch or case statement.
RTL labels generated for loops and conditionals don't go through here;
they are generated directly at the RTL level, by other functions below.
Note that this has nothing to do with defining label *names*.
Languages vary in how they do that and what that even means. */
void
expand_label (tree label)
{
rtx label_r = label_rtx (label);
do_pending_stack_adjust ();
emit_label (label_r);
if (DECL_NAME (label))
LABEL_NAME (DECL_RTL (label)) = IDENTIFIER_POINTER (DECL_NAME (label));
if (DECL_NONLOCAL (label))
{
expand_nl_goto_receiver ();
nonlocal_goto_handler_labels
= gen_rtx_EXPR_LIST (VOIDmode, label_r,
nonlocal_goto_handler_labels);
}
if (FORCED_LABEL (label))
forced_labels = gen_rtx_EXPR_LIST (VOIDmode, label_r, forced_labels);
if (DECL_NONLOCAL (label) || FORCED_LABEL (label))
maybe_set_first_label_num (label_r);
}
/* Generate RTL code for a `goto' statement with target label LABEL.
LABEL should be a LABEL_DECL tree node that was or will later be
defined with `expand_label'. */
void
expand_goto (tree label)
{
#ifdef ENABLE_CHECKING
/* Check for a nonlocal goto to a containing function. Should have
gotten translated to __builtin_nonlocal_goto. */
tree context = decl_function_context (label);
gcc_assert (!context || context == current_function_decl);
#endif
emit_jump (label_rtx (label));
}
/* Return the number of times character C occurs in string S. */
static int
n_occurrences (int c, const char *s)
{
int n = 0;
while (*s)
n += (*s++ == c);
return n;
}
/* Generate RTL for an asm statement (explicit assembler code).
STRING is a STRING_CST node containing the assembler code text,
or an ADDR_EXPR containing a STRING_CST. VOL nonzero means the
insn is volatile; don't optimize it. */
static void
expand_asm_loc (tree string, int vol, location_t locus)
{
rtx body;
if (TREE_CODE (string) == ADDR_EXPR)
string = TREE_OPERAND (string, 0);
body = gen_rtx_ASM_INPUT_loc (VOIDmode,
ggc_strdup (TREE_STRING_POINTER (string)),
locus);
MEM_VOLATILE_P (body) = vol;
emit_insn (body);
}
/* Parse the output constraint pointed to by *CONSTRAINT_P. It is the
OPERAND_NUMth output operand, indexed from zero. There are NINPUTS
inputs and NOUTPUTS outputs to this extended-asm. Upon return,
*ALLOWS_MEM will be TRUE iff the constraint allows the use of a
memory operand. Similarly, *ALLOWS_REG will be TRUE iff the
constraint allows the use of a register operand. And, *IS_INOUT
will be true if the operand is read-write, i.e., if it is used as
an input as well as an output. If *CONSTRAINT_P is not in
canonical form, it will be made canonical. (Note that `+' will be
replaced with `=' as part of this process.)
Returns TRUE if all went well; FALSE if an error occurred. */
bool
parse_output_constraint (const char **constraint_p, int operand_num,
int ninputs, int noutputs, bool *allows_mem,
bool *allows_reg, bool *is_inout)
{
const char *constraint = *constraint_p;
const char *p;
/* Assume the constraint doesn't allow the use of either a register
or memory. */
*allows_mem = false;
*allows_reg = false;
/* Allow the `=' or `+' to not be at the beginning of the string,
since it wasn't explicitly documented that way, and there is a
large body of code that puts it last. Swap the character to
the front, so as not to uglify any place else. */
p = strchr (constraint, '=');
if (!p)
p = strchr (constraint, '+');
/* If the string doesn't contain an `=', issue an error
message. */
if (!p)
{
error ("output operand constraint lacks %<=%>");
return false;
}
/* If the constraint begins with `+', then the operand is both read
from and written to. */
*is_inout = (*p == '+');
/* Canonicalize the output constraint so that it begins with `='. */
if (p != constraint || *is_inout)
{
char *buf;
size_t c_len = strlen (constraint);
if (p != constraint)
warning (0, "output constraint %qc for operand %d "
"is not at the beginning",
*p, operand_num);
/* Make a copy of the constraint. */
buf = XALLOCAVEC (char, c_len + 1);
strcpy (buf, constraint);
/* Swap the first character and the `=' or `+'. */
buf[p - constraint] = buf[0];
/* Make sure the first character is an `='. (Until we do this,
it might be a `+'.) */
buf[0] = '=';
/* Replace the constraint with the canonicalized string. */
*constraint_p = ggc_alloc_string (buf, c_len);
constraint = *constraint_p;
}
/* Loop through the constraint string. */
for (p = constraint + 1; *p; p += CONSTRAINT_LEN (*p, p))
switch (*p)
{
case '+':
case '=':
error ("operand constraint contains incorrectly positioned "
"%<+%> or %<=%>");
return false;
case '%':
if (operand_num + 1 == ninputs + noutputs)
{
error ("%<%%%> constraint used with last operand");
return false;
}
break;
case 'V': case TARGET_MEM_CONSTRAINT: case 'o':
*allows_mem = true;
break;
case '?': case '!': case '*': case '&': case '#':
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L': case 'M':
case 'N': case 'O': case 'P': case ',':
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '[':
error ("matching constraint not valid in output operand");
return false;
case '<': case '>':
/* ??? Before flow, auto inc/dec insns are not supposed to exist,
excepting those that expand_call created. So match memory
and hope. */
*allows_mem = true;
break;
case 'g': case 'X':
*allows_reg = true;
*allows_mem = true;
break;
case 'p': case 'r':
*allows_reg = true;
break;
default:
if (!ISALPHA (*p))
break;
if (REG_CLASS_FROM_CONSTRAINT (*p, p) != NO_REGS)
*allows_reg = true;
#ifdef EXTRA_CONSTRAINT_STR
else if (EXTRA_ADDRESS_CONSTRAINT (*p, p))
*allows_reg = true;
else if (EXTRA_MEMORY_CONSTRAINT (*p, p))
*allows_mem = true;
else
{
/* Otherwise we can't assume anything about the nature of
the constraint except that it isn't purely registers.
Treat it like "g" and hope for the best. */
*allows_reg = true;
*allows_mem = true;
}
#endif
break;
}
return true;
}
/* Similar, but for input constraints. */
bool
parse_input_constraint (const char **constraint_p, int input_num,
int ninputs, int noutputs, int ninout,
const char * const * constraints,
bool *allows_mem, bool *allows_reg)
{
const char *constraint = *constraint_p;
const char *orig_constraint = constraint;
size_t c_len = strlen (constraint);
size_t j;
bool saw_match = false;
/* Assume the constraint doesn't allow the use of either
a register or memory. */
*allows_mem = false;
*allows_reg = false;
/* Make sure constraint has neither `=', `+', nor '&'. */
for (j = 0; j < c_len; j += CONSTRAINT_LEN (constraint[j], constraint+j))
switch (constraint[j])
{
case '+': case '=': case '&':
if (constraint == orig_constraint)
{
error ("input operand constraint contains %qc", constraint[j]);
return false;
}
break;
case '%':
if (constraint == orig_constraint
&& input_num + 1 == ninputs - ninout)
{
error ("%<%%%> constraint used with last operand");
return false;
}
break;
case 'V': case TARGET_MEM_CONSTRAINT: case 'o':
*allows_mem = true;
break;
case '<': case '>':
case '?': case '!': case '*': case '#':
case 'E': case 'F': case 'G': case 'H':
case 's': case 'i': case 'n':
case 'I': case 'J': case 'K': case 'L': case 'M':
case 'N': case 'O': case 'P': case ',':
break;
/* Whether or not a numeric constraint allows a register is
decided by the matching constraint, and so there is no need
to do anything special with them. We must handle them in
the default case, so that we don't unnecessarily force
operands to memory. */
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
char *end;
unsigned long match;
saw_match = true;
match = strtoul (constraint + j, &end, 10);
if (match >= (unsigned long) noutputs)
{
error ("matching constraint references invalid operand number");
return false;
}
/* Try and find the real constraint for this dup. Only do this
if the matching constraint is the only alternative. */
if (*end == '\0'
&& (j == 0 || (j == 1 && constraint[0] == '%')))
{
constraint = constraints[match];
*constraint_p = constraint;
c_len = strlen (constraint);
j = 0;
/* ??? At the end of the loop, we will skip the first part of
the matched constraint. This assumes not only that the
other constraint is an output constraint, but also that
the '=' or '+' come first. */
break;
}
else
j = end - constraint;
/* Anticipate increment at end of loop. */
j--;
}
/* Fall through. */
case 'p': case 'r':
*allows_reg = true;
break;
case 'g': case 'X':
*allows_reg = true;
*allows_mem = true;
break;
default:
if (! ISALPHA (constraint[j]))
{
error ("invalid punctuation %qc in constraint", constraint[j]);
return false;
}
if (REG_CLASS_FROM_CONSTRAINT (constraint[j], constraint + j)
!= NO_REGS)
*allows_reg = true;
#ifdef EXTRA_CONSTRAINT_STR
else if (EXTRA_ADDRESS_CONSTRAINT (constraint[j], constraint + j))
*allows_reg = true;
else if (EXTRA_MEMORY_CONSTRAINT (constraint[j], constraint + j))
*allows_mem = true;
else
{
/* Otherwise we can't assume anything about the nature of
the constraint except that it isn't purely registers.
Treat it like "g" and hope for the best. */
*allows_reg = true;
*allows_mem = true;
}
#endif
break;
}
if (saw_match && !*allows_reg)
warning (0, "matching constraint does not allow a register");
return true;
}
/* Return DECL iff there's an overlap between *REGS and DECL, where DECL
can be an asm-declared register. Called via walk_tree. */
static tree
decl_overlaps_hard_reg_set_p (tree *declp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data)
{
tree decl = *declp;
const HARD_REG_SET *const regs = (const HARD_REG_SET *) data;
if (TREE_CODE (decl) == VAR_DECL)
{
if (DECL_HARD_REGISTER (decl)
&& REG_P (DECL_RTL (decl))
&& REGNO (DECL_RTL (decl)) < FIRST_PSEUDO_REGISTER)
{
rtx reg = DECL_RTL (decl);
if (overlaps_hard_reg_set_p (*regs, GET_MODE (reg), REGNO (reg)))
return decl;
}
walk_subtrees = 0;
}
else if (TYPE_P (decl) || TREE_CODE (decl) == PARM_DECL)
walk_subtrees = 0;
return NULL_TREE;
}
/* If there is an overlap between *REGS and DECL, return the first overlap
found. */
tree
tree_overlaps_hard_reg_set (tree decl, HARD_REG_SET *regs)
{
return walk_tree (&decl, decl_overlaps_hard_reg_set_p, regs, NULL);
}
/* Check for overlap between registers marked in CLOBBERED_REGS and
anything inappropriate in T. Emit error and return the register
variable definition for error, NULL_TREE for ok. */
static bool
tree_conflicts_with_clobbers_p (tree t, HARD_REG_SET *clobbered_regs)
{
/* Conflicts between asm-declared register variables and the clobber
list are not allowed. */
tree overlap = tree_overlaps_hard_reg_set (t, clobbered_regs);
if (overlap)
{
error ("asm-specifier for variable %qE conflicts with asm clobber list",
DECL_NAME (overlap));
/* Reset registerness to stop multiple errors emitted for a single
variable. */
DECL_REGISTER (overlap) = 0;
return true;
}
return false;
}
/* Generate RTL for an asm statement with arguments.
STRING is the instruction template.
OUTPUTS is a list of output arguments (lvalues); INPUTS a list of inputs.
Each output or input has an expression in the TREE_VALUE and
a tree list in TREE_PURPOSE which in turn contains a constraint
name in TREE_VALUE (or NULL_TREE) and a constraint string
in TREE_PURPOSE.
CLOBBERS is a list of STRING_CST nodes each naming a hard register
that is clobbered by this insn.
Not all kinds of lvalue that may appear in OUTPUTS can be stored directly.
Some elements of OUTPUTS may be replaced with trees representing temporary
values. The caller should copy those temporary values to the originally
specified lvalues.
VOL nonzero means the insn is volatile; don't optimize it. */
static void
expand_asm_operands (tree string, tree outputs, tree inputs,
tree clobbers, tree labels, int vol, location_t locus)
{
rtvec argvec, constraintvec, labelvec;
rtx body;
int ninputs = list_length (inputs);
int noutputs = list_length (outputs);
int nlabels = list_length (labels);
int ninout;
int nclobbers;
HARD_REG_SET clobbered_regs;
int clobber_conflict_found = 0;
tree tail;
tree t;
int i;
/* Vector of RTX's of evaluated output operands. */
rtx *output_rtx = XALLOCAVEC (rtx, noutputs);
int *inout_opnum = XALLOCAVEC (int, noutputs);
rtx *real_output_rtx = XALLOCAVEC (rtx, noutputs);
enum machine_mode *inout_mode = XALLOCAVEC (enum machine_mode, noutputs);
const char **constraints = XALLOCAVEC (const char *, noutputs + ninputs);
int old_generating_concat_p = generating_concat_p;
/* An ASM with no outputs needs to be treated as volatile, for now. */
if (noutputs == 0)
vol = 1;
if (! check_operand_nalternatives (outputs, inputs))
return;
string = resolve_asm_operand_names (string, outputs, inputs, labels);
/* Collect constraints. */
i = 0;
for (t = outputs; t ; t = TREE_CHAIN (t), i++)
constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
for (t = inputs; t ; t = TREE_CHAIN (t), i++)
constraints[i] = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (t)));
/* Sometimes we wish to automatically clobber registers across an asm.
Case in point is when the i386 backend moved from cc0 to a hard reg --
maintaining source-level compatibility means automatically clobbering
the flags register. */
clobbers = targetm.md_asm_clobbers (outputs, inputs, clobbers);
/* Count the number of meaningful clobbered registers, ignoring what
we would ignore later. */
nclobbers = 0;
CLEAR_HARD_REG_SET (clobbered_regs);
for (tail = clobbers; tail; tail = TREE_CHAIN (tail))
{
const char *regname;
int nregs;
if (TREE_VALUE (tail) == error_mark_node)
return;
regname = TREE_STRING_POINTER (TREE_VALUE (tail));
i = decode_reg_name_and_count (regname, &nregs);
if (i == -4)
++nclobbers;
else if (i == -2)
error ("unknown register name %qs in %<asm%>", regname);
/* Mark clobbered registers. */
if (i >= 0)
{
int reg;
for (reg = i; reg < i + nregs; reg++)
{
++nclobbers;
/* Clobbering the PIC register is an error. */
if (reg == (int) PIC_OFFSET_TABLE_REGNUM)
{
error ("PIC register clobbered by %qs in %<asm%>", regname);
return;
}
SET_HARD_REG_BIT (clobbered_regs, reg);
}
}
}
/* First pass over inputs and outputs checks validity and sets
mark_addressable if needed. */
ninout = 0;
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
{
tree val = TREE_VALUE (tail);
tree type = TREE_TYPE (val);
const char *constraint;
bool is_inout;
bool allows_reg;
bool allows_mem;
/* If there's an erroneous arg, emit no insn. */
if (type == error_mark_node)
return;
/* Try to parse the output constraint. If that fails, there's
no point in going further. */
constraint = constraints[i];
if (!parse_output_constraint (&constraint, i, ninputs, noutputs,
&allows_mem, &allows_reg, &is_inout))
return;
if (! allows_reg
&& (allows_mem
|| is_inout
|| (DECL_P (val)
&& REG_P (DECL_RTL (val))
&& GET_MODE (DECL_RTL (val)) != TYPE_MODE (type))))
mark_addressable (val);
if (is_inout)
ninout++;
}
ninputs += ninout;
if (ninputs + noutputs > MAX_RECOG_OPERANDS)
{
error ("more than %d operands in %<asm%>", MAX_RECOG_OPERANDS);
return;
}
for (i = 0, tail = inputs; tail; i++, tail = TREE_CHAIN (tail))
{
bool allows_reg, allows_mem;
const char *constraint;
/* If there's an erroneous arg, emit no insn, because the ASM_INPUT
would get VOIDmode and that could cause a crash in reload. */
if (TREE_TYPE (TREE_VALUE (tail)) == error_mark_node)
return;
constraint = constraints[i + noutputs];
if (! parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
constraints, &allows_mem, &allows_reg))
return;
if (! allows_reg && allows_mem)
mark_addressable (TREE_VALUE (tail));
}
/* Second pass evaluates arguments. */
/* Make sure stack is consistent for asm goto. */
if (nlabels > 0)
do_pending_stack_adjust ();
ninout = 0;
for (i = 0, tail = outputs; tail; tail = TREE_CHAIN (tail), i++)
{
tree val = TREE_VALUE (tail);
tree type = TREE_TYPE (val);
bool is_inout;
bool allows_reg;
bool allows_mem;
rtx op;
bool ok;
ok = parse_output_constraint (&constraints[i], i, ninputs,
noutputs, &allows_mem, &allows_reg,
&is_inout);
gcc_assert (ok);
/* If an output operand is not a decl or indirect ref and our constraint
allows a register, make a temporary to act as an intermediate.
Make the asm insn write into that, then our caller will copy it to
the real output operand. Likewise for promoted variables. */
generating_concat_p = 0;
real_output_rtx[i] = NULL_RTX;
if ((TREE_CODE (val) == INDIRECT_REF
&& allows_mem)
|| (DECL_P (val)
&& (allows_mem || REG_P (DECL_RTL (val)))
&& ! (REG_P (DECL_RTL (val))
&& GET_MODE (DECL_RTL (val)) != TYPE_MODE (type)))
|| ! allows_reg
|| is_inout)
{
op = expand_expr (val, NULL_RTX, VOIDmode, EXPAND_WRITE);
if (MEM_P (op))
op = validize_mem (op);
if (! allows_reg && !MEM_P (op))
error ("output number %d not directly addressable", i);
if ((! allows_mem && MEM_P (op))
|| GET_CODE (op) == CONCAT)
{
real_output_rtx[i] = op;
op = gen_reg_rtx (GET_MODE (op));
if (is_inout)
emit_move_insn (op, real_output_rtx[i]);
}
}
else
{
op = assign_temp (type, 0, 0, 1);
op = validize_mem (op);
if (!MEM_P (op) && TREE_CODE (TREE_VALUE (tail)) == SSA_NAME)
set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (TREE_VALUE (tail)), op);
TREE_VALUE (tail) = make_tree (type, op);
}
output_rtx[i] = op;
generating_concat_p = old_generating_concat_p;
if (is_inout)
{
inout_mode[ninout] = TYPE_MODE (type);
inout_opnum[ninout++] = i;
}
if (tree_conflicts_with_clobbers_p (val, &clobbered_regs))
clobber_conflict_found = 1;
}
/* Make vectors for the expression-rtx, constraint strings,
and named operands. */
argvec = rtvec_alloc (ninputs);
constraintvec = rtvec_alloc (ninputs);
labelvec = rtvec_alloc (nlabels);
body = gen_rtx_ASM_OPERANDS ((noutputs == 0 ? VOIDmode
: GET_MODE (output_rtx[0])),
ggc_strdup (TREE_STRING_POINTER (string)),
empty_string, 0, argvec, constraintvec,
labelvec, locus);
MEM_VOLATILE_P (body) = vol;
/* Eval the inputs and put them into ARGVEC.
Put their constraints into ASM_INPUTs and store in CONSTRAINTS. */
for (i = 0, tail = inputs; tail; tail = TREE_CHAIN (tail), ++i)
{
bool allows_reg, allows_mem;
const char *constraint;
tree val, type;
rtx op;
bool ok;
constraint = constraints[i + noutputs];
ok = parse_input_constraint (&constraint, i, ninputs, noutputs, ninout,
constraints, &allows_mem, &allows_reg);
gcc_assert (ok);
generating_concat_p = 0;
val = TREE_VALUE (tail);
type = TREE_TYPE (val);
/* EXPAND_INITIALIZER will not generate code for valid initializer
constants, but will still generate code for other types of operand.
This is the behavior we want for constant constraints. */
op = expand_expr (val, NULL_RTX, VOIDmode,
allows_reg ? EXPAND_NORMAL
: allows_mem ? EXPAND_MEMORY
: EXPAND_INITIALIZER);
/* Never pass a CONCAT to an ASM. */
if (GET_CODE (op) == CONCAT)
op = force_reg (GET_MODE (op), op);
else if (MEM_P (op))
op = validize_mem (op);
if (asm_operand_ok (op, constraint, NULL) <= 0)
{
if (allows_reg && TYPE_MODE (type) != BLKmode)
op = force_reg (TYPE_MODE (type), op);
else if (!allows_mem)
warning (0, "asm operand %d probably doesn%'t match constraints",
i + noutputs);
else if (MEM_P (op))
{
/* We won't recognize either volatile memory or memory
with a queued address as available a memory_operand
at this point. Ignore it: clearly this *is* a memory. */
}
else
{
warning (0, "use of memory input without lvalue in "
"asm operand %d is deprecated", i + noutputs);
if (CONSTANT_P (op))
{
rtx mem = force_const_mem (TYPE_MODE (type), op);
if (mem)
op = validize_mem (mem);
else
op = force_reg (TYPE_MODE (type), op);
}
if (REG_P (op)
|| GET_CODE (op) == SUBREG
|| GET_CODE (op) == CONCAT)
{
tree qual_type = build_qualified_type (type,
(TYPE_QUALS (type)
| TYPE_QUAL_CONST));
rtx memloc = assign_temp (qual_type, 1, 1, 1);
memloc = validize_mem (memloc);
emit_move_insn (memloc, op);
op = memloc;
}
}
}
generating_concat_p = old_generating_concat_p;
ASM_OPERANDS_INPUT (body, i) = op;
ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, i)
= gen_rtx_ASM_INPUT (TYPE_MODE (type),
ggc_strdup (constraints[i + noutputs]));
if (tree_conflicts_with_clobbers_p (val, &clobbered_regs))
clobber_conflict_found = 1;
}
/* Protect all the operands from the queue now that they have all been
evaluated. */
generating_concat_p = 0;
/* For in-out operands, copy output rtx to input rtx. */
for (i = 0; i < ninout; i++)
{
int j = inout_opnum[i];
char buffer[16];
ASM_OPERANDS_INPUT (body, ninputs - ninout + i)
= output_rtx[j];
sprintf (buffer, "%d", j);
ASM_OPERANDS_INPUT_CONSTRAINT_EXP (body, ninputs - ninout + i)
= gen_rtx_ASM_INPUT (inout_mode[i], ggc_strdup (buffer));
}
/* Copy labels to the vector. */
for (i = 0, tail = labels; i < nlabels; ++i, tail = TREE_CHAIN (tail))
ASM_OPERANDS_LABEL (body, i)
= gen_rtx_LABEL_REF (Pmode, label_rtx (TREE_VALUE (tail)));
generating_concat_p = old_generating_concat_p;
/* Now, for each output, construct an rtx
(set OUTPUT (asm_operands INSN OUTPUTCONSTRAINT OUTPUTNUMBER
ARGVEC CONSTRAINTS OPNAMES))
If there is more than one, put them inside a PARALLEL. */
if (nlabels > 0 && nclobbers == 0)
{
gcc_assert (noutputs == 0);
emit_jump_insn (body);
}
else if (noutputs == 0 && nclobbers == 0)
{
/* No output operands: put in a raw ASM_OPERANDS rtx. */
emit_insn (body);