Skip to content

Commit 259f150

Browse files
author
Michael Kuperstein
committedOct 7, 2015
[X86] Emit .cfi_escape GNU_ARGS_SIZE when adjusting the stack before calls
When outgoing function arguments are passed using push instructions, and EH is enabled, we may need to indicate to the stack unwinder that the stack pointer was adjusted before the call. This should fix the exception handling issues in PR24792. Differential Revision: http://reviews.llvm.org/D13132 llvm-svn: 249522
1 parent 1a6fd1c commit 259f150

File tree

9 files changed

+257
-4
lines changed

9 files changed

+257
-4
lines changed
 

Diff for: ‎llvm/include/llvm/MC/MCDwarf.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,8 @@ class MCCFIInstruction {
340340
OpRestore,
341341
OpUndefined,
342342
OpRegister,
343-
OpWindowSave
343+
OpWindowSave,
344+
OpGnuArgsSize
344345
};
345346

346347
private:
@@ -454,6 +455,11 @@ class MCCFIInstruction {
454455
return MCCFIInstruction(OpEscape, L, 0, 0, Vals);
455456
}
456457

458+
/// \brief A special wrapper for .cfi_escape that indicates GNU_ARGS_SIZE
459+
static MCCFIInstruction createGnuArgsSize(MCSymbol *L, int Size) {
460+
return MCCFIInstruction(OpGnuArgsSize, L, 0, Size, "");
461+
}
462+
457463
OpType getOperation() const { return Operation; }
458464
MCSymbol *getLabel() const { return Label; }
459465

@@ -473,7 +479,7 @@ class MCCFIInstruction {
473479
int getOffset() const {
474480
assert(Operation == OpDefCfa || Operation == OpOffset ||
475481
Operation == OpRelOffset || Operation == OpDefCfaOffset ||
476-
Operation == OpAdjustCfaOffset);
482+
Operation == OpAdjustCfaOffset || Operation == OpGnuArgsSize);
477483
return Offset;
478484
}
479485

Diff for: ‎llvm/include/llvm/MC/MCStreamer.h

+1
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ class MCStreamer {
661661
virtual void EmitCFIRelOffset(int64_t Register, int64_t Offset);
662662
virtual void EmitCFIAdjustCfaOffset(int64_t Adjustment);
663663
virtual void EmitCFIEscape(StringRef Values);
664+
virtual void EmitCFIGnuArgsSize(int64_t Size);
664665
virtual void EmitCFISignalFrame();
665666
virtual void EmitCFIUndefined(int64_t Register);
666667
virtual void EmitCFIRegister(int64_t Register1, int64_t Register2);

Diff for: ‎llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,12 @@ void AsmPrinter::emitCFIInstruction(const MCCFIInstruction &Inst) const {
246246
case MCCFIInstruction::OpSameValue:
247247
OutStreamer->EmitCFISameValue(Inst.getRegister());
248248
break;
249+
case MCCFIInstruction::OpGnuArgsSize:
250+
OutStreamer->EmitCFIGnuArgsSize(Inst.getOffset());
251+
break;
252+
case MCCFIInstruction::OpEscape:
253+
OutStreamer->EmitCFIEscape(Inst.getValues());
254+
break;
249255
}
250256
}
251257

Diff for: ‎llvm/lib/MC/MCAsmStreamer.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "llvm/Support/ErrorHandling.h"
3030
#include "llvm/Support/Format.h"
3131
#include "llvm/Support/FormattedStream.h"
32+
#include "llvm/Support/LEB128.h"
3233
#include "llvm/Support/MathExtras.h"
3334
#include "llvm/Support/Path.h"
3435
#include <cctype>
@@ -210,6 +211,8 @@ class MCAsmStreamer final : public MCStreamer {
210211
void EmitCFISameValue(int64_t Register) override;
211212
void EmitCFIRelOffset(int64_t Register, int64_t Offset) override;
212213
void EmitCFIAdjustCfaOffset(int64_t Adjustment) override;
214+
void EmitCFIEscape(StringRef Values) override;
215+
void EmitCFIGnuArgsSize(int64_t Size) override;
213216
void EmitCFISignalFrame() override;
214217
void EmitCFIUndefined(int64_t Register) override;
215218
void EmitCFIRegister(int64_t Register1, int64_t Register2) override;
@@ -1010,6 +1013,32 @@ void MCAsmStreamer::EmitCFIDefCfaOffset(int64_t Offset) {
10101013
EmitEOL();
10111014
}
10121015

1016+
static void PrintCFIEscape(llvm::formatted_raw_ostream &OS, StringRef Values) {
1017+
OS << "\t.cfi_escape ";
1018+
if (!Values.empty()) {
1019+
size_t e = Values.size() - 1;
1020+
for (size_t i = 0; i < e; ++i)
1021+
OS << format("0x%02x", uint8_t(Values[i])) << ", ";
1022+
OS << format("0x%02x", uint8_t(Values[e]));
1023+
}
1024+
}
1025+
1026+
void MCAsmStreamer::EmitCFIEscape(StringRef Values) {
1027+
MCStreamer::EmitCFIEscape(Values);
1028+
PrintCFIEscape(OS, Values);
1029+
EmitEOL();
1030+
}
1031+
1032+
void MCAsmStreamer::EmitCFIGnuArgsSize(int64_t Size) {
1033+
MCStreamer::EmitCFIGnuArgsSize(Size);
1034+
1035+
uint8_t Buffer[16] = { dwarf::DW_CFA_GNU_args_size };
1036+
unsigned Len = encodeULEB128(Size, Buffer + 1) + 1;
1037+
1038+
PrintCFIEscape(OS, StringRef((const char *)&Buffer[0], Len));
1039+
EmitEOL();
1040+
}
1041+
10131042
void MCAsmStreamer::EmitCFIDefCfaRegister(int64_t Register) {
10141043
MCStreamer::EmitCFIDefCfaRegister(Register);
10151044
OS << "\t.cfi_def_cfa_register ";

Diff for: ‎llvm/lib/MC/MCDwarf.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,11 @@ void FrameEmitterImpl::EmitCFIInstruction(MCObjectStreamer &Streamer,
11471147
Streamer.EmitIntValue(dwarf::DW_CFA_restore | Reg, 1);
11481148
return;
11491149
}
1150+
case MCCFIInstruction::OpGnuArgsSize: {
1151+
Streamer.EmitIntValue(dwarf::DW_CFA_GNU_args_size, 1);
1152+
Streamer.EmitULEB128IntValue(Instr.getOffset());
1153+
return;
1154+
}
11501155
case MCCFIInstruction::OpEscape:
11511156
Streamer.EmitBytes(Instr.getValues());
11521157
return;

Diff for: ‎llvm/lib/MC/MCStreamer.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,14 @@ void MCStreamer::EmitCFIEscape(StringRef Values) {
359359
CurFrame->Instructions.push_back(Instruction);
360360
}
361361

362+
void MCStreamer::EmitCFIGnuArgsSize(int64_t Size) {
363+
MCSymbol *Label = EmitCFICommon();
364+
MCCFIInstruction Instruction =
365+
MCCFIInstruction::createGnuArgsSize(Label, Size);
366+
MCDwarfFrameInfo *CurFrame = getCurrentDwarfFrameInfo();
367+
CurFrame->Instructions.push_back(Instruction);
368+
}
369+
362370
void MCStreamer::EmitCFISignalFrame() {
363371
EnsureValidDwarfFrame();
364372
MCDwarfFrameInfo *CurFrame = getCurrentDwarfFrameInfo();

Diff for: ‎llvm/lib/Target/X86/X86FrameLowering.cpp

+19-2
Original file line numberDiff line numberDiff line change
@@ -2073,15 +2073,32 @@ eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
20732073
// If the stack pointer can be changed after prologue, turn the
20742074
// adjcallstackup instruction into a 'sub ESP, <amt>' and the
20752075
// adjcallstackdown instruction into 'add ESP, <amt>'
2076-
if (Amount == 0)
2077-
return;
20782076

20792077
// We need to keep the stack aligned properly. To do this, we round the
20802078
// amount of space needed for the outgoing arguments up to the next
20812079
// alignment boundary.
20822080
unsigned StackAlign = getStackAlignment();
20832081
Amount = RoundUpToAlignment(Amount, StackAlign);
20842082

2083+
// If we have any exception handlers in this function, and we adjust
2084+
// the SP before calls, we may need to indicate this to the unwinder,
2085+
// using GNU_ARGS_SIZE. Note that this may be necessary
2086+
// even when Amount == 0, because the preceding function may have
2087+
// set a non-0 GNU_ARGS_SIZE.
2088+
// TODO: We don't need to reset this between subsequent functions,
2089+
// if it didn't change.
2090+
bool HasDwarfEHHandlers =
2091+
!MF.getTarget().getMCAsmInfo()->usesWindowsCFI() &&
2092+
!MF.getMMI().getLandingPads().empty();
2093+
2094+
if (HasDwarfEHHandlers && !isDestroy &&
2095+
MF.getInfo<X86MachineFunctionInfo>()->getHasPushSequences())
2096+
BuildCFI(MBB, I, DL,
2097+
MCCFIInstruction::createGnuArgsSize(nullptr, Amount));
2098+
2099+
if (Amount == 0)
2100+
return;
2101+
20852102
// Factor out the amount that gets handled inside the sequence
20862103
// (Pushes of argument for frame setup, callee pops for frame destroy)
20872104
Amount -= InternalAmt;

Diff for: ‎llvm/test/CodeGen/X86/push-cfi-obj.ll

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
; RUN: llc < %s -mtriple=i686-pc-linux -filetype=obj | llvm-readobj -s -sr -sd | FileCheck %s
2+
3+
; CHECK: Index: 8
4+
; CHECK-NEXT: Name: .eh_frame (41)
5+
; CHECK-NEXT: Type: SHT_PROGBITS (0x1)
6+
; CHECK-NEXT: Flags [ (0x2)
7+
; CHECK-NEXT: SHF_ALLOC (0x2)
8+
; CHECK-NEXT: ]
9+
; CHECK-NEXT: Address: 0x0
10+
; CHECK-NEXT: Offset: 0x64
11+
; CHECK-NEXT: Size: 60
12+
; CHECK-NEXT: Link: 0
13+
; CHECK-NEXT: Info: 0
14+
; CHECK-NEXT: AddressAlignment: 4
15+
; CHECK-NEXT: EntrySize: 0
16+
; CHECK-NEXT: Relocations [
17+
; CHECK-NEXT: ]
18+
; CHECK-NEXT: SectionData (
19+
; CHECK-NEXT: 0000: 1C000000 00000000 017A504C 5200017C |.........zPLR..||
20+
; CHECK-NEXT: 0010: 08070000 00000000 1B0C0404 88010000 |................|
21+
; CHECK-NEXT: 0020: 18000000 24000000 00000000 19000000 |....$...........|
22+
; CHECK-NEXT: 0030: 04000000 00430E10 2E100000 |.....C......|
23+
; CHECK-NEXT: )
24+
25+
declare i32 @__gxx_personality_v0(...)
26+
declare void @good(i32 %a, i32 %b, i32 %c, i32 %d)
27+
28+
define void @test() optsize personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
29+
entry:
30+
invoke void @good(i32 1, i32 2, i32 3, i32 4)
31+
to label %continue unwind label %cleanup
32+
continue:
33+
ret void
34+
cleanup:
35+
landingpad { i8*, i32 }
36+
cleanup
37+
ret void
38+
}

Diff for: ‎llvm/test/CodeGen/X86/push-cfi.ll

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
; RUN: llc < %s -mtriple=i686-pc-linux | FileCheck %s
2+
3+
declare i32 @__gxx_personality_v0(...)
4+
declare void @good(i32 %a, i32 %b, i32 %c, i32 %d)
5+
declare void @large(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e, i32 %f)
6+
declare void @empty()
7+
8+
; We use an invoke, and expect a .cfi_escape GNU_ARGS_SIZE with size 16
9+
; before the invocation
10+
; CHECK-LABEL: test1:
11+
; CHECK: .cfi_escape 0x2e, 0x10
12+
; CHECK-NEXT: pushl $4
13+
; CHECK-NEXT: pushl $3
14+
; CHECK-NEXT: pushl $2
15+
; CHECK-NEXT: pushl $1
16+
; CHECK-NEXT: call
17+
; CHECK-NEXT: addl $16, %esp
18+
define void @test1() optsize personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
19+
entry:
20+
invoke void @good(i32 1, i32 2, i32 3, i32 4)
21+
to label %continue unwind label %cleanup
22+
continue:
23+
ret void
24+
cleanup:
25+
landingpad { i8*, i32 }
26+
cleanup
27+
ret void
28+
}
29+
30+
; If the function has no handlers, we don't need to generate GNU_ARGS_SIZE,
31+
; even if it has an unwind table.
32+
; CHECK-LABEL: test2:
33+
; CHECK-NOT: .cfi_escape
34+
; CHECK: pushl $4
35+
; CHECK-NEXT: pushl $3
36+
; CHECK-NEXT: pushl $2
37+
; CHECK-NEXT: pushl $1
38+
; CHECK-NEXT: call
39+
; CHECK-NEXT: addl $16, %esp
40+
define void @test2() optsize personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
41+
entry:
42+
call void @good(i32 1, i32 2, i32 3, i32 4)
43+
ret void
44+
}
45+
46+
; If we did not end up using any pushes, no need for GNU_ARGS_SIZE anywhere
47+
; CHECK-LABEL: test3:
48+
; CHECK-NOT: .cfi_escape
49+
; CHECK-NOT: pushl
50+
; CHECK: retl
51+
define void @test3() optsize personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
52+
entry:
53+
invoke void @empty()
54+
to label %continue unwind label %cleanup
55+
continue:
56+
ret void
57+
cleanup:
58+
landingpad { i8*, i32 }
59+
cleanup
60+
ret void
61+
}
62+
63+
; Different sized stacks need different GNU_ARGS_SIZEs
64+
; CHECK-LABEL: test4:
65+
; CHECK: .cfi_escape 0x2e, 0x10
66+
; CHECK-NEXT: pushl $4
67+
; CHECK-NEXT: pushl $3
68+
; CHECK-NEXT: pushl $2
69+
; CHECK-NEXT: pushl $1
70+
; CHECK-NEXT: call
71+
; CHECK-NEXT: addl $16, %esp
72+
; CHECK: .cfi_escape 0x2e, 0x20
73+
; CHECK-NEXT: subl $8, %esp
74+
; CHECK-NEXT: pushl $11
75+
; CHECK-NEXT: pushl $10
76+
; CHECK-NEXT: pushl $9
77+
; CHECK-NEXT: pushl $8
78+
; CHECK-NEXT: pushl $7
79+
; CHECK-NEXT: pushl $6
80+
; CHECK-NEXT: calll large
81+
; CHECK-NEXT: addl $32, %esp
82+
define void @test4() optsize personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
83+
entry:
84+
invoke void @good(i32 1, i32 2, i32 3, i32 4)
85+
to label %continue1 unwind label %cleanup
86+
continue1:
87+
invoke void @large(i32 6, i32 7, i32 8, i32 9, i32 10, i32 11)
88+
to label %continue2 unwind label %cleanup
89+
continue2:
90+
ret void
91+
cleanup:
92+
landingpad { i8*, i32 }
93+
cleanup
94+
ret void
95+
}
96+
97+
; If we did use pushes, we need to reset GNU_ARGS_SIZE before a call
98+
; without parameters
99+
; CHECK-LABEL: test5:
100+
; CHECK: .cfi_escape 0x2e, 0x10
101+
; CHECK-NEXT: pushl $4
102+
; CHECK-NEXT: pushl $3
103+
; CHECK-NEXT: pushl $2
104+
; CHECK-NEXT: pushl $1
105+
; CHECK-NEXT: call
106+
; CHECK-NEXT: addl $16, %esp
107+
; CHECK: .cfi_escape 0x2e, 0x00
108+
; CHECK-NEXT: call
109+
define void @test5() optsize personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
110+
entry:
111+
invoke void @good(i32 1, i32 2, i32 3, i32 4)
112+
to label %continue1 unwind label %cleanup
113+
continue1:
114+
invoke void @empty()
115+
to label %continue2 unwind label %cleanup
116+
continue2:
117+
ret void
118+
cleanup:
119+
landingpad { i8*, i32 }
120+
cleanup
121+
ret void
122+
}
123+
124+
; This is actually inefficient - we don't need to repeat the .cfi_escape twice.
125+
; CHECK-LABEL: test6:
126+
; CHECK: .cfi_escape 0x2e, 0x10
127+
; CHECK: call
128+
; CHECK: .cfi_escape 0x2e, 0x10
129+
; CHECK: call
130+
define void @test6() optsize personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
131+
entry:
132+
invoke void @good(i32 1, i32 2, i32 3, i32 4)
133+
to label %continue1 unwind label %cleanup
134+
continue1:
135+
invoke void @good(i32 5, i32 6, i32 7, i32 8)
136+
to label %continue2 unwind label %cleanup
137+
continue2:
138+
ret void
139+
cleanup:
140+
landingpad { i8*, i32 }
141+
cleanup
142+
ret void
143+
}

0 commit comments

Comments
 (0)
Please sign in to comment.