forked from riscvarchive/riscv-gcc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdwarf2cfi.c
3636 lines (3030 loc) · 99.6 KB
/
dwarf2cfi.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
/* Dwarf2 Call Frame Information helper routines.
Copyright (C) 1992-2020 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/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "target.h"
#include "function.h"
#include "rtl.h"
#include "tree.h"
#include "tree-pass.h"
#include "memmodel.h"
#include "tm_p.h"
#include "emit-rtl.h"
#include "stor-layout.h"
#include "cfgbuild.h"
#include "dwarf2out.h"
#include "dwarf2asm.h"
#include "common/common-target.h"
#include "except.h" /* expand_builtin_dwarf_sp_column */
#include "profile-count.h" /* For expr.h */
#include "expr.h" /* init_return_column_size */
#include "output.h" /* asm_out_file */
#include "debug.h" /* dwarf2out_do_frame, dwarf2out_do_cfi_asm */
/* ??? Poison these here until it can be done generically. They've been
totally replaced in this file; make sure it stays that way. */
#undef DWARF2_UNWIND_INFO
#undef DWARF2_FRAME_INFO
#if (GCC_VERSION >= 3000)
#pragma GCC poison DWARF2_UNWIND_INFO DWARF2_FRAME_INFO
#endif
#ifndef INCOMING_RETURN_ADDR_RTX
#define INCOMING_RETURN_ADDR_RTX (gcc_unreachable (), NULL_RTX)
#endif
#ifndef DEFAULT_INCOMING_FRAME_SP_OFFSET
#define DEFAULT_INCOMING_FRAME_SP_OFFSET INCOMING_FRAME_SP_OFFSET
#endif
/* A collected description of an entire row of the abstract CFI table. */
struct GTY(()) dw_cfi_row
{
/* The expression that computes the CFA, expressed in two different ways.
The CFA member for the simple cases, and the full CFI expression for
the complex cases. The later will be a DW_CFA_cfa_expression. */
dw_cfa_location cfa;
dw_cfi_ref cfa_cfi;
/* The expressions for any register column that is saved. */
cfi_vec reg_save;
/* True if the register window is saved. */
bool window_save;
/* True if the return address is in a mangled state. */
bool ra_mangled;
};
/* The caller's ORIG_REG is saved in SAVED_IN_REG. */
struct GTY(()) reg_saved_in_data {
rtx orig_reg;
rtx saved_in_reg;
};
/* Since we no longer have a proper CFG, we're going to create a facsimile
of one on the fly while processing the frame-related insns.
We create dw_trace_info structures for each extended basic block beginning
and ending at a "save point". Save points are labels, barriers, certain
notes, and of course the beginning and end of the function.
As we encounter control transfer insns, we propagate the "current"
row state across the edges to the starts of traces. When checking is
enabled, we validate that we propagate the same data from all sources.
All traces are members of the TRACE_INFO array, in the order in which
they appear in the instruction stream.
All save points are present in the TRACE_INDEX hash, mapping the insn
starting a trace to the dw_trace_info describing the trace. */
struct dw_trace_info
{
/* The insn that begins the trace. */
rtx_insn *head;
/* The row state at the beginning and end of the trace. */
dw_cfi_row *beg_row, *end_row;
/* Tracking for DW_CFA_GNU_args_size. The "true" sizes are those we find
while scanning insns. However, the args_size value is irrelevant at
any point except can_throw_internal_p insns. Therefore the "delay"
sizes the values that must actually be emitted for this trace. */
poly_int64_pod beg_true_args_size, end_true_args_size;
poly_int64_pod beg_delay_args_size, end_delay_args_size;
/* The first EH insn in the trace, where beg_delay_args_size must be set. */
rtx_insn *eh_head;
/* The following variables contain data used in interpreting frame related
expressions. These are not part of the "real" row state as defined by
Dwarf, but it seems like they need to be propagated into a trace in case
frame related expressions have been sunk. */
/* ??? This seems fragile. These variables are fragments of a larger
expression. If we do not keep the entire expression together, we risk
not being able to put it together properly. Consider forcing targets
to generate self-contained expressions and dropping all of the magic
interpretation code in this file. Or at least refusing to shrink wrap
any frame related insn that doesn't contain a complete expression. */
/* The register used for saving registers to the stack, and its offset
from the CFA. */
dw_cfa_location cfa_store;
/* A temporary register holding an integral value used in adjusting SP
or setting up the store_reg. The "offset" field holds the integer
value, not an offset. */
dw_cfa_location cfa_temp;
/* A set of registers saved in other registers. This is the inverse of
the row->reg_save info, if the entry is a DW_CFA_register. This is
implemented as a flat array because it normally contains zero or 1
entry, depending on the target. IA-64 is the big spender here, using
a maximum of 5 entries. */
vec<reg_saved_in_data> regs_saved_in_regs;
/* An identifier for this trace. Used only for debugging dumps. */
unsigned id;
/* True if this trace immediately follows NOTE_INSN_SWITCH_TEXT_SECTIONS. */
bool switch_sections;
/* True if we've seen different values incoming to beg_true_args_size. */
bool args_size_undefined;
/* True if we've seen an insn with a REG_ARGS_SIZE note before EH_HEAD. */
bool args_size_defined_for_eh;
};
/* Hashtable helpers. */
struct trace_info_hasher : nofree_ptr_hash <dw_trace_info>
{
static inline hashval_t hash (const dw_trace_info *);
static inline bool equal (const dw_trace_info *, const dw_trace_info *);
};
inline hashval_t
trace_info_hasher::hash (const dw_trace_info *ti)
{
return INSN_UID (ti->head);
}
inline bool
trace_info_hasher::equal (const dw_trace_info *a, const dw_trace_info *b)
{
return a->head == b->head;
}
/* The variables making up the pseudo-cfg, as described above. */
static vec<dw_trace_info> trace_info;
static vec<dw_trace_info *> trace_work_list;
static hash_table<trace_info_hasher> *trace_index;
/* A vector of call frame insns for the CIE. */
cfi_vec cie_cfi_vec;
/* The state of the first row of the FDE table, which includes the
state provided by the CIE. */
static GTY(()) dw_cfi_row *cie_cfi_row;
static GTY(()) reg_saved_in_data *cie_return_save;
static GTY(()) unsigned long dwarf2out_cfi_label_num;
/* The insn after which a new CFI note should be emitted. */
static rtx_insn *add_cfi_insn;
/* When non-null, add_cfi will add the CFI to this vector. */
static cfi_vec *add_cfi_vec;
/* The current instruction trace. */
static dw_trace_info *cur_trace;
/* The current, i.e. most recently generated, row of the CFI table. */
static dw_cfi_row *cur_row;
/* A copy of the current CFA, for use during the processing of a
single insn. */
static dw_cfa_location *cur_cfa;
/* We delay emitting a register save until either (a) we reach the end
of the prologue or (b) the register is clobbered. This clusters
register saves so that there are fewer pc advances. */
struct queued_reg_save {
rtx reg;
rtx saved_reg;
poly_int64_pod cfa_offset;
};
static vec<queued_reg_save> queued_reg_saves;
/* True if any CFI directives were emitted at the current insn. */
static bool any_cfis_emitted;
/* Short-hand for commonly used register numbers. */
static unsigned dw_stack_pointer_regnum;
static unsigned dw_frame_pointer_regnum;
/* Hook used by __throw. */
rtx
expand_builtin_dwarf_sp_column (void)
{
unsigned int dwarf_regnum = DWARF_FRAME_REGNUM (STACK_POINTER_REGNUM);
return GEN_INT (DWARF2_FRAME_REG_OUT (dwarf_regnum, 1));
}
/* MEM is a memory reference for the register size table, each element of
which has mode MODE. Initialize column C as a return address column. */
static void
init_return_column_size (scalar_int_mode mode, rtx mem, unsigned int c)
{
HOST_WIDE_INT offset = c * GET_MODE_SIZE (mode);
HOST_WIDE_INT size = GET_MODE_SIZE (Pmode);
emit_move_insn (adjust_address (mem, mode, offset),
gen_int_mode (size, mode));
}
/* Datastructure used by expand_builtin_init_dwarf_reg_sizes and
init_one_dwarf_reg_size to communicate on what has been done by the
latter. */
struct init_one_dwarf_reg_state
{
/* Whether the dwarf return column was initialized. */
bool wrote_return_column;
/* For each hard register REGNO, whether init_one_dwarf_reg_size
was given REGNO to process already. */
bool processed_regno [FIRST_PSEUDO_REGISTER];
};
/* Helper for expand_builtin_init_dwarf_reg_sizes. Generate code to
initialize the dwarf register size table entry corresponding to register
REGNO in REGMODE. TABLE is the table base address, SLOTMODE is the mode to
use for the size entry to initialize, and INIT_STATE is the communication
datastructure conveying what we're doing to our caller. */
static
void init_one_dwarf_reg_size (int regno, machine_mode regmode,
rtx table, machine_mode slotmode,
init_one_dwarf_reg_state *init_state)
{
const unsigned int dnum = DWARF_FRAME_REGNUM (regno);
const unsigned int rnum = DWARF2_FRAME_REG_OUT (dnum, 1);
const unsigned int dcol = DWARF_REG_TO_UNWIND_COLUMN (rnum);
poly_int64 slotoffset = dcol * GET_MODE_SIZE (slotmode);
poly_int64 regsize = GET_MODE_SIZE (regmode);
init_state->processed_regno[regno] = true;
if (rnum >= DWARF_FRAME_REGISTERS)
return;
if (dnum == DWARF_FRAME_RETURN_COLUMN)
{
if (regmode == VOIDmode)
return;
init_state->wrote_return_column = true;
}
/* ??? When is this true? Should it be a test based on DCOL instead? */
if (maybe_lt (slotoffset, 0))
return;
emit_move_insn (adjust_address (table, slotmode, slotoffset),
gen_int_mode (regsize, slotmode));
}
/* Generate code to initialize the dwarf register size table located
at the provided ADDRESS. */
void
expand_builtin_init_dwarf_reg_sizes (tree address)
{
unsigned int i;
scalar_int_mode mode = SCALAR_INT_TYPE_MODE (char_type_node);
rtx addr = expand_normal (address);
rtx mem = gen_rtx_MEM (BLKmode, addr);
init_one_dwarf_reg_state init_state;
memset ((char *)&init_state, 0, sizeof (init_state));
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
{
machine_mode save_mode;
rtx span;
/* No point in processing a register multiple times. This could happen
with register spans, e.g. when a reg is first processed as a piece of
a span, then as a register on its own later on. */
if (init_state.processed_regno[i])
continue;
save_mode = targetm.dwarf_frame_reg_mode (i);
span = targetm.dwarf_register_span (gen_rtx_REG (save_mode, i));
if (!span)
init_one_dwarf_reg_size (i, save_mode, mem, mode, &init_state);
else
{
for (int si = 0; si < XVECLEN (span, 0); si++)
{
rtx reg = XVECEXP (span, 0, si);
init_one_dwarf_reg_size
(REGNO (reg), GET_MODE (reg), mem, mode, &init_state);
}
}
}
if (!init_state.wrote_return_column)
init_return_column_size (mode, mem, DWARF_FRAME_RETURN_COLUMN);
#ifdef DWARF_ALT_FRAME_RETURN_COLUMN
init_return_column_size (mode, mem, DWARF_ALT_FRAME_RETURN_COLUMN);
#endif
targetm.init_dwarf_reg_sizes_extra (address);
}
static dw_trace_info *
get_trace_info (rtx_insn *insn)
{
dw_trace_info dummy;
dummy.head = insn;
return trace_index->find_with_hash (&dummy, INSN_UID (insn));
}
static bool
save_point_p (rtx_insn *insn)
{
/* Labels, except those that are really jump tables. */
if (LABEL_P (insn))
return inside_basic_block_p (insn);
/* We split traces at the prologue/epilogue notes because those
are points at which the unwind info is usually stable. This
makes it easier to find spots with identical unwind info so
that we can use remember/restore_state opcodes. */
if (NOTE_P (insn))
switch (NOTE_KIND (insn))
{
case NOTE_INSN_PROLOGUE_END:
case NOTE_INSN_EPILOGUE_BEG:
return true;
}
return false;
}
/* Divide OFF by DWARF_CIE_DATA_ALIGNMENT, asserting no remainder. */
static inline HOST_WIDE_INT
div_data_align (HOST_WIDE_INT off)
{
HOST_WIDE_INT r = off / DWARF_CIE_DATA_ALIGNMENT;
gcc_assert (r * DWARF_CIE_DATA_ALIGNMENT == off);
return r;
}
/* Return true if we need a signed version of a given opcode
(e.g. DW_CFA_offset_extended_sf vs DW_CFA_offset_extended). */
static inline bool
need_data_align_sf_opcode (HOST_WIDE_INT off)
{
return DWARF_CIE_DATA_ALIGNMENT < 0 ? off > 0 : off < 0;
}
/* Return a pointer to a newly allocated Call Frame Instruction. */
static inline dw_cfi_ref
new_cfi (void)
{
dw_cfi_ref cfi = ggc_alloc<dw_cfi_node> ();
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = 0;
cfi->dw_cfi_oprnd2.dw_cfi_reg_num = 0;
return cfi;
}
/* Return a newly allocated CFI row, with no defined data. */
static dw_cfi_row *
new_cfi_row (void)
{
dw_cfi_row *row = ggc_cleared_alloc<dw_cfi_row> ();
row->cfa.reg = INVALID_REGNUM;
return row;
}
/* Return a copy of an existing CFI row. */
static dw_cfi_row *
copy_cfi_row (dw_cfi_row *src)
{
dw_cfi_row *dst = ggc_alloc<dw_cfi_row> ();
*dst = *src;
dst->reg_save = vec_safe_copy (src->reg_save);
return dst;
}
/* Return a copy of an existing CFA location. */
static dw_cfa_location *
copy_cfa (dw_cfa_location *src)
{
dw_cfa_location *dst = ggc_alloc<dw_cfa_location> ();
*dst = *src;
return dst;
}
/* Generate a new label for the CFI info to refer to. */
static char *
dwarf2out_cfi_label (void)
{
int num = dwarf2out_cfi_label_num++;
char label[20];
ASM_GENERATE_INTERNAL_LABEL (label, "LCFI", num);
return xstrdup (label);
}
/* Add CFI either to the current insn stream or to a vector, or both. */
static void
add_cfi (dw_cfi_ref cfi)
{
any_cfis_emitted = true;
if (add_cfi_insn != NULL)
{
add_cfi_insn = emit_note_after (NOTE_INSN_CFI, add_cfi_insn);
NOTE_CFI (add_cfi_insn) = cfi;
}
if (add_cfi_vec != NULL)
vec_safe_push (*add_cfi_vec, cfi);
}
static void
add_cfi_args_size (poly_int64 size)
{
/* We don't yet have a representation for polynomial sizes. */
HOST_WIDE_INT const_size = size.to_constant ();
dw_cfi_ref cfi = new_cfi ();
/* While we can occasionally have args_size < 0 internally, this state
should not persist at a point we actually need an opcode. */
gcc_assert (const_size >= 0);
cfi->dw_cfi_opc = DW_CFA_GNU_args_size;
cfi->dw_cfi_oprnd1.dw_cfi_offset = const_size;
add_cfi (cfi);
}
static void
add_cfi_restore (unsigned reg)
{
dw_cfi_ref cfi = new_cfi ();
cfi->dw_cfi_opc = (reg & ~0x3f ? DW_CFA_restore_extended : DW_CFA_restore);
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
add_cfi (cfi);
}
/* Perform ROW->REG_SAVE[COLUMN] = CFI. CFI may be null, indicating
that the register column is no longer saved. */
static void
update_row_reg_save (dw_cfi_row *row, unsigned column, dw_cfi_ref cfi)
{
if (vec_safe_length (row->reg_save) <= column)
vec_safe_grow_cleared (row->reg_save, column + 1);
(*row->reg_save)[column] = cfi;
}
/* This function fills in aa dw_cfa_location structure from a dwarf location
descriptor sequence. */
static void
get_cfa_from_loc_descr (dw_cfa_location *cfa, struct dw_loc_descr_node *loc)
{
struct dw_loc_descr_node *ptr;
cfa->offset = 0;
cfa->base_offset = 0;
cfa->indirect = 0;
cfa->reg = -1;
for (ptr = loc; ptr != NULL; ptr = ptr->dw_loc_next)
{
enum dwarf_location_atom op = ptr->dw_loc_opc;
switch (op)
{
case DW_OP_reg0:
case DW_OP_reg1:
case DW_OP_reg2:
case DW_OP_reg3:
case DW_OP_reg4:
case DW_OP_reg5:
case DW_OP_reg6:
case DW_OP_reg7:
case DW_OP_reg8:
case DW_OP_reg9:
case DW_OP_reg10:
case DW_OP_reg11:
case DW_OP_reg12:
case DW_OP_reg13:
case DW_OP_reg14:
case DW_OP_reg15:
case DW_OP_reg16:
case DW_OP_reg17:
case DW_OP_reg18:
case DW_OP_reg19:
case DW_OP_reg20:
case DW_OP_reg21:
case DW_OP_reg22:
case DW_OP_reg23:
case DW_OP_reg24:
case DW_OP_reg25:
case DW_OP_reg26:
case DW_OP_reg27:
case DW_OP_reg28:
case DW_OP_reg29:
case DW_OP_reg30:
case DW_OP_reg31:
cfa->reg = op - DW_OP_reg0;
break;
case DW_OP_regx:
cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
break;
case DW_OP_breg0:
case DW_OP_breg1:
case DW_OP_breg2:
case DW_OP_breg3:
case DW_OP_breg4:
case DW_OP_breg5:
case DW_OP_breg6:
case DW_OP_breg7:
case DW_OP_breg8:
case DW_OP_breg9:
case DW_OP_breg10:
case DW_OP_breg11:
case DW_OP_breg12:
case DW_OP_breg13:
case DW_OP_breg14:
case DW_OP_breg15:
case DW_OP_breg16:
case DW_OP_breg17:
case DW_OP_breg18:
case DW_OP_breg19:
case DW_OP_breg20:
case DW_OP_breg21:
case DW_OP_breg22:
case DW_OP_breg23:
case DW_OP_breg24:
case DW_OP_breg25:
case DW_OP_breg26:
case DW_OP_breg27:
case DW_OP_breg28:
case DW_OP_breg29:
case DW_OP_breg30:
case DW_OP_breg31:
cfa->reg = op - DW_OP_breg0;
cfa->base_offset = ptr->dw_loc_oprnd1.v.val_int;
break;
case DW_OP_bregx:
cfa->reg = ptr->dw_loc_oprnd1.v.val_int;
cfa->base_offset = ptr->dw_loc_oprnd2.v.val_int;
break;
case DW_OP_deref:
cfa->indirect = 1;
break;
case DW_OP_plus_uconst:
cfa->offset = ptr->dw_loc_oprnd1.v.val_unsigned;
break;
default:
gcc_unreachable ();
}
}
}
/* Find the previous value for the CFA, iteratively. CFI is the opcode
to interpret, *LOC will be updated as necessary, *REMEMBER is used for
one level of remember/restore state processing. */
void
lookup_cfa_1 (dw_cfi_ref cfi, dw_cfa_location *loc, dw_cfa_location *remember)
{
switch (cfi->dw_cfi_opc)
{
case DW_CFA_def_cfa_offset:
case DW_CFA_def_cfa_offset_sf:
loc->offset = cfi->dw_cfi_oprnd1.dw_cfi_offset;
break;
case DW_CFA_def_cfa_register:
loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
break;
case DW_CFA_def_cfa:
case DW_CFA_def_cfa_sf:
loc->reg = cfi->dw_cfi_oprnd1.dw_cfi_reg_num;
loc->offset = cfi->dw_cfi_oprnd2.dw_cfi_offset;
break;
case DW_CFA_def_cfa_expression:
if (cfi->dw_cfi_oprnd2.dw_cfi_cfa_loc)
*loc = *cfi->dw_cfi_oprnd2.dw_cfi_cfa_loc;
else
get_cfa_from_loc_descr (loc, cfi->dw_cfi_oprnd1.dw_cfi_loc);
break;
case DW_CFA_remember_state:
gcc_assert (!remember->in_use);
*remember = *loc;
remember->in_use = 1;
break;
case DW_CFA_restore_state:
gcc_assert (remember->in_use);
*loc = *remember;
remember->in_use = 0;
break;
default:
break;
}
}
/* Determine if two dw_cfa_location structures define the same data. */
bool
cfa_equal_p (const dw_cfa_location *loc1, const dw_cfa_location *loc2)
{
return (loc1->reg == loc2->reg
&& known_eq (loc1->offset, loc2->offset)
&& loc1->indirect == loc2->indirect
&& (loc1->indirect == 0
|| known_eq (loc1->base_offset, loc2->base_offset)));
}
/* Determine if two CFI operands are identical. */
static bool
cfi_oprnd_equal_p (enum dw_cfi_oprnd_type t, dw_cfi_oprnd *a, dw_cfi_oprnd *b)
{
switch (t)
{
case dw_cfi_oprnd_unused:
return true;
case dw_cfi_oprnd_reg_num:
return a->dw_cfi_reg_num == b->dw_cfi_reg_num;
case dw_cfi_oprnd_offset:
return a->dw_cfi_offset == b->dw_cfi_offset;
case dw_cfi_oprnd_addr:
return (a->dw_cfi_addr == b->dw_cfi_addr
|| strcmp (a->dw_cfi_addr, b->dw_cfi_addr) == 0);
case dw_cfi_oprnd_loc:
return loc_descr_equal_p (a->dw_cfi_loc, b->dw_cfi_loc);
case dw_cfi_oprnd_cfa_loc:
return cfa_equal_p (a->dw_cfi_cfa_loc, b->dw_cfi_cfa_loc);
}
gcc_unreachable ();
}
/* Determine if two CFI entries are identical. */
static bool
cfi_equal_p (dw_cfi_ref a, dw_cfi_ref b)
{
enum dwarf_call_frame_info opc;
/* Make things easier for our callers, including missing operands. */
if (a == b)
return true;
if (a == NULL || b == NULL)
return false;
/* Obviously, the opcodes must match. */
opc = a->dw_cfi_opc;
if (opc != b->dw_cfi_opc)
return false;
/* Compare the two operands, re-using the type of the operands as
already exposed elsewhere. */
return (cfi_oprnd_equal_p (dw_cfi_oprnd1_desc (opc),
&a->dw_cfi_oprnd1, &b->dw_cfi_oprnd1)
&& cfi_oprnd_equal_p (dw_cfi_oprnd2_desc (opc),
&a->dw_cfi_oprnd2, &b->dw_cfi_oprnd2));
}
/* Determine if two CFI_ROW structures are identical. */
static bool
cfi_row_equal_p (dw_cfi_row *a, dw_cfi_row *b)
{
size_t i, n_a, n_b, n_max;
if (a->cfa_cfi)
{
if (!cfi_equal_p (a->cfa_cfi, b->cfa_cfi))
return false;
}
else if (!cfa_equal_p (&a->cfa, &b->cfa))
return false;
n_a = vec_safe_length (a->reg_save);
n_b = vec_safe_length (b->reg_save);
n_max = MAX (n_a, n_b);
for (i = 0; i < n_max; ++i)
{
dw_cfi_ref r_a = NULL, r_b = NULL;
if (i < n_a)
r_a = (*a->reg_save)[i];
if (i < n_b)
r_b = (*b->reg_save)[i];
if (!cfi_equal_p (r_a, r_b))
return false;
}
if (a->window_save != b->window_save)
return false;
if (a->ra_mangled != b->ra_mangled)
return false;
return true;
}
/* The CFA is now calculated from NEW_CFA. Consider OLD_CFA in determining
what opcode to emit. Returns the CFI opcode to effect the change, or
NULL if NEW_CFA == OLD_CFA. */
static dw_cfi_ref
def_cfa_0 (dw_cfa_location *old_cfa, dw_cfa_location *new_cfa)
{
dw_cfi_ref cfi;
/* If nothing changed, no need to issue any call frame instructions. */
if (cfa_equal_p (old_cfa, new_cfa))
return NULL;
cfi = new_cfi ();
HOST_WIDE_INT const_offset;
if (new_cfa->reg == old_cfa->reg
&& !new_cfa->indirect
&& !old_cfa->indirect
&& new_cfa->offset.is_constant (&const_offset))
{
/* Construct a "DW_CFA_def_cfa_offset <offset>" instruction, indicating
the CFA register did not change but the offset did. The data
factoring for DW_CFA_def_cfa_offset_sf happens in output_cfi, or
in the assembler via the .cfi_def_cfa_offset directive. */
if (const_offset < 0)
cfi->dw_cfi_opc = DW_CFA_def_cfa_offset_sf;
else
cfi->dw_cfi_opc = DW_CFA_def_cfa_offset;
cfi->dw_cfi_oprnd1.dw_cfi_offset = const_offset;
}
else if (new_cfa->offset.is_constant ()
&& known_eq (new_cfa->offset, old_cfa->offset)
&& old_cfa->reg != INVALID_REGNUM
&& !new_cfa->indirect
&& !old_cfa->indirect)
{
/* Construct a "DW_CFA_def_cfa_register <register>" instruction,
indicating the CFA register has changed to <register> but the
offset has not changed. This requires the old CFA to have
been set as a register plus offset rather than a general
DW_CFA_def_cfa_expression. */
cfi->dw_cfi_opc = DW_CFA_def_cfa_register;
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
}
else if (new_cfa->indirect == 0
&& new_cfa->offset.is_constant (&const_offset))
{
/* Construct a "DW_CFA_def_cfa <register> <offset>" instruction,
indicating the CFA register has changed to <register> with
the specified offset. The data factoring for DW_CFA_def_cfa_sf
happens in output_cfi, or in the assembler via the .cfi_def_cfa
directive. */
if (const_offset < 0)
cfi->dw_cfi_opc = DW_CFA_def_cfa_sf;
else
cfi->dw_cfi_opc = DW_CFA_def_cfa;
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = new_cfa->reg;
cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
}
else
{
/* Construct a DW_CFA_def_cfa_expression instruction to
calculate the CFA using a full location expression since no
register-offset pair is available. */
struct dw_loc_descr_node *loc_list;
cfi->dw_cfi_opc = DW_CFA_def_cfa_expression;
loc_list = build_cfa_loc (new_cfa, 0);
cfi->dw_cfi_oprnd1.dw_cfi_loc = loc_list;
if (!new_cfa->offset.is_constant ()
|| !new_cfa->base_offset.is_constant ())
/* It's hard to reconstruct the CFA location for a polynomial
expression, so just cache it instead. */
cfi->dw_cfi_oprnd2.dw_cfi_cfa_loc = copy_cfa (new_cfa);
else
cfi->dw_cfi_oprnd2.dw_cfi_cfa_loc = NULL;
}
return cfi;
}
/* Similarly, but take OLD_CFA from CUR_ROW, and update it after the fact. */
static void
def_cfa_1 (dw_cfa_location *new_cfa)
{
dw_cfi_ref cfi;
if (cur_trace->cfa_store.reg == new_cfa->reg && new_cfa->indirect == 0)
cur_trace->cfa_store.offset = new_cfa->offset;
cfi = def_cfa_0 (&cur_row->cfa, new_cfa);
if (cfi)
{
cur_row->cfa = *new_cfa;
cur_row->cfa_cfi = (cfi->dw_cfi_opc == DW_CFA_def_cfa_expression
? cfi : NULL);
add_cfi (cfi);
}
}
/* Add the CFI for saving a register. REG is the CFA column number.
If SREG is -1, the register is saved at OFFSET from the CFA;
otherwise it is saved in SREG. */
static void
reg_save (unsigned int reg, unsigned int sreg, poly_int64 offset)
{
dw_fde_ref fde = cfun ? cfun->fde : NULL;
dw_cfi_ref cfi = new_cfi ();
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
if (sreg == INVALID_REGNUM)
{
HOST_WIDE_INT const_offset;
/* When stack is aligned, store REG using DW_CFA_expression with FP. */
if (fde && fde->stack_realign)
{
cfi->dw_cfi_opc = DW_CFA_expression;
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
cfi->dw_cfi_oprnd2.dw_cfi_loc
= build_cfa_aligned_loc (&cur_row->cfa, offset,
fde->stack_realignment);
}
else if (offset.is_constant (&const_offset))
{
if (need_data_align_sf_opcode (const_offset))
cfi->dw_cfi_opc = DW_CFA_offset_extended_sf;
else if (reg & ~0x3f)
cfi->dw_cfi_opc = DW_CFA_offset_extended;
else
cfi->dw_cfi_opc = DW_CFA_offset;
cfi->dw_cfi_oprnd2.dw_cfi_offset = const_offset;
}
else
{
cfi->dw_cfi_opc = DW_CFA_expression;
cfi->dw_cfi_oprnd1.dw_cfi_reg_num = reg;
cfi->dw_cfi_oprnd2.dw_cfi_loc
= build_cfa_loc (&cur_row->cfa, offset);
}
}
else if (sreg == reg)
{
/* While we could emit something like DW_CFA_same_value or
DW_CFA_restore, we never expect to see something like that
in a prologue. This is more likely to be a bug. A backend
can always bypass this by using REG_CFA_RESTORE directly. */
gcc_unreachable ();
}
else
{
cfi->dw_cfi_opc = DW_CFA_register;
cfi->dw_cfi_oprnd2.dw_cfi_reg_num = sreg;
}
add_cfi (cfi);
update_row_reg_save (cur_row, reg, cfi);
}
/* A subroutine of scan_trace. Check INSN for a REG_ARGS_SIZE note
and adjust data structures to match. */
static void
notice_args_size (rtx_insn *insn)
{
poly_int64 args_size, delta;
rtx note;
note = find_reg_note (insn, REG_ARGS_SIZE, NULL);
if (note == NULL)
return;
if (!cur_trace->eh_head)
cur_trace->args_size_defined_for_eh = true;
args_size = get_args_size (note);
delta = args_size - cur_trace->end_true_args_size;
if (known_eq (delta, 0))
return;
cur_trace->end_true_args_size = args_size;
/* If the CFA is computed off the stack pointer, then we must adjust
the computation of the CFA as well. */
if (cur_cfa->reg == dw_stack_pointer_regnum)
{
gcc_assert (!cur_cfa->indirect);
/* Convert a change in args_size (always a positive in the
direction of stack growth) to a change in stack pointer. */
if (!STACK_GROWS_DOWNWARD)
delta = -delta;
cur_cfa->offset += delta;
}
}
/* A subroutine of scan_trace. INSN is can_throw_internal. Update the
data within the trace related to EH insns and args_size. */
static void
notice_eh_throw (rtx_insn *insn)
{
poly_int64 args_size = cur_trace->end_true_args_size;
if (cur_trace->eh_head == NULL)
{
cur_trace->eh_head = insn;
cur_trace->beg_delay_args_size = args_size;
cur_trace->end_delay_args_size = args_size;
}
else if (maybe_ne (cur_trace->end_delay_args_size, args_size))
{
cur_trace->end_delay_args_size = args_size;