summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp14
-rw-r--r--llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h2
-rw-r--r--llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp13
-rw-r--r--llvm/lib/Target/X86/MCTargetDesc/X86TargetStreamer.h1
-rw-r--r--llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp55
-rw-r--r--llvm/lib/Target/X86/X86FrameLowering.cpp7
-rw-r--r--llvm/lib/Target/X86/X86InstrCompiler.td2
-rw-r--r--llvm/lib/Target/X86/X86MCInstLower.cpp4
-rw-r--r--llvm/test/DebugInfo/COFF/fpo-realign-alloca.ll3
-rw-r--r--llvm/test/DebugInfo/COFF/fpo-realign-vframe.ll230
-rw-r--r--llvm/test/MC/COFF/cv-fpo-realign.s199
11 files changed, 520 insertions, 10 deletions
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
index aaa1dc95757..707ee410d76 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp
@@ -1255,6 +1255,7 @@ void CodeViewDebug::beginFunctionImpl(const MachineFunction *MF) {
// instruction (AArch64), this will be zero.
CurFn->CSRSize = MFI.getCVBytesOfCalleeSavedRegisters();
CurFn->FrameSize = MFI.getStackSize();
+ CurFn->HasStackRealignment = TRI->needsStackRealignment(*MF);
// For this function S_FRAMEPROC record, figure out which codeview register
// will be the frame pointer.
@@ -1267,7 +1268,7 @@ void CodeViewDebug::beginFunctionImpl(const MachineFunction *MF) {
} else {
// If there is an FP, parameters are always relative to it.
CurFn->EncodedParamFramePtrReg = EncodedFramePtrReg::FramePtr;
- if (TRI->needsStackRealignment(*MF)) {
+ if (CurFn->HasStackRealignment) {
// If the stack needs realignment, locals are relative to SP or VFRAME.
CurFn->EncodedLocalFramePtrReg = EncodedFramePtrReg::StackPtr;
} else {
@@ -2502,13 +2503,18 @@ void CodeViewDebug::emitLocalVariable(const FunctionInfo &FI,
int Offset = DefRange.DataOffset;
unsigned Reg = DefRange.CVRegister;
- // x86 call sequences often use PUSH instructions, which disrupt
+ // 32-bit x86 call sequences often use PUSH instructions, which disrupt
// ESP-relative offsets. Use the virtual frame pointer, VFRAME or $T0,
- // instead. In simple cases, $T0 will be the CFA. If the frame required
- // re-alignment, it will be the CFA aligned downwards.
+ // instead. In simple cases, $T0 will be the CFA.
if (RegisterId(Reg) == RegisterId::ESP) {
Reg = unsigned(RegisterId::VFRAME);
Offset -= FI.FrameSize;
+
+ // If the frame requires realignment, VFRAME will be ESP after it is
+ // aligned. We have to remove the ESP adjustments made to push CSRs and
+ // EBP. EBP is not included in CSRSize.
+ if (FI.HasStackRealignment)
+ Offset += FI.CSRSize + 4;
}
// If we can use the chosen frame pointer for the frame and this isn't a
diff --git a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
index 3836862f0a8..b2864306e50 100644
--- a/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
+++ b/llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h
@@ -165,6 +165,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
codeview::FrameProcedureOptions FrameProcOpts;
+ bool HasStackRealignment = false;
+
bool HaveLineInfo = false;
};
FunctionInfo *CurFn = nullptr;
diff --git a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
index 8b7b250e1a0..e67daa5d857 100644
--- a/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
+++ b/llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp
@@ -852,6 +852,7 @@ private:
bool parseDirectiveFPOSetFrame(SMLoc L);
bool parseDirectiveFPOPushReg(SMLoc L);
bool parseDirectiveFPOStackAlloc(SMLoc L);
+ bool parseDirectiveFPOStackAlign(SMLoc L);
bool parseDirectiveFPOEndPrologue(SMLoc L);
bool parseDirectiveFPOEndProc(SMLoc L);
bool parseDirectiveFPOData(SMLoc L);
@@ -3315,6 +3316,8 @@ bool X86AsmParser::ParseDirective(AsmToken DirectiveID) {
return parseDirectiveFPOPushReg(DirectiveID.getLoc());
else if (IDVal == ".cv_fpo_stackalloc")
return parseDirectiveFPOStackAlloc(DirectiveID.getLoc());
+ else if (IDVal == ".cv_fpo_stackalign")
+ return parseDirectiveFPOStackAlign(DirectiveID.getLoc());
else if (IDVal == ".cv_fpo_endprologue")
return parseDirectiveFPOEndPrologue(DirectiveID.getLoc());
else if (IDVal == ".cv_fpo_endproc")
@@ -3429,6 +3432,16 @@ bool X86AsmParser::parseDirectiveFPOStackAlloc(SMLoc L) {
return getTargetStreamer().emitFPOStackAlloc(Offset, L);
}
+// .cv_fpo_stackalign 8
+bool X86AsmParser::parseDirectiveFPOStackAlign(SMLoc L) {
+ MCAsmParser &Parser = getParser();
+ int64_t Offset;
+ if (Parser.parseIntToken(Offset, "expected offset") ||
+ Parser.parseEOL("unexpected tokens"))
+ return addErrorSuffix(" in '.cv_fpo_stackalign' directive");
+ return getTargetStreamer().emitFPOStackAlign(Offset, L);
+}
+
// .cv_fpo_endprologue
bool X86AsmParser::parseDirectiveFPOEndPrologue(SMLoc L) {
MCAsmParser &Parser = getParser();
diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86TargetStreamer.h b/llvm/lib/Target/X86/MCTargetDesc/X86TargetStreamer.h
index 8d38cd32b82..10a282dd296 100644
--- a/llvm/lib/Target/X86/MCTargetDesc/X86TargetStreamer.h
+++ b/llvm/lib/Target/X86/MCTargetDesc/X86TargetStreamer.h
@@ -26,6 +26,7 @@ public:
virtual bool emitFPOData(const MCSymbol *ProcSym, SMLoc L = {}) = 0;
virtual bool emitFPOPushReg(unsigned Reg, SMLoc L = {}) = 0;
virtual bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L = {}) = 0;
+ virtual bool emitFPOStackAlign(unsigned Align, SMLoc L = {}) = 0;
virtual bool emitFPOSetFrame(unsigned Reg, SMLoc L = {}) = 0;
};
diff --git a/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp b/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
index 093dab4f2f9..bee9b704633 100644
--- a/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
+++ b/llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp
@@ -38,6 +38,7 @@ public:
bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
+ bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
};
@@ -47,6 +48,7 @@ struct FPOInstruction {
enum Operation {
PushReg,
StackAlloc,
+ StackAlign,
SetFrame,
} Op;
unsigned RegOrOffset;
@@ -90,6 +92,7 @@ public:
bool emitFPOData(const MCSymbol *ProcSym, SMLoc L) override;
bool emitFPOPushReg(unsigned Reg, SMLoc L) override;
bool emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) override;
+ bool emitFPOStackAlign(unsigned Align, SMLoc L) override;
bool emitFPOSetFrame(unsigned Reg, SMLoc L) override;
};
} // end namespace
@@ -133,6 +136,11 @@ bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc,
return false;
}
+bool X86WinCOFFAsmTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
+ OS << "\t.cv_fpo_stackalign\t" << Align << '\n';
+ return false;
+}
+
bool X86WinCOFFAsmTargetStreamer::emitFPOSetFrame(unsigned Reg, SMLoc L) {
OS << "\t.cv_fpo_setframe\t";
InstPrinter.printRegName(OS, Reg);
@@ -226,6 +234,24 @@ bool X86WinCOFFTargetStreamer::emitFPOStackAlloc(unsigned StackAlloc, SMLoc L) {
return false;
}
+bool X86WinCOFFTargetStreamer::emitFPOStackAlign(unsigned Align, SMLoc L) {
+ if (checkInFPOPrologue(L))
+ return true;
+ if (!llvm::any_of(CurFPOData->Instructions, [](const FPOInstruction &Inst) {
+ return Inst.Op == FPOInstruction::SetFrame;
+ })) {
+ getContext().reportError(
+ L, "a frame register must be established before aligning the stack");
+ return true;
+ }
+ FPOInstruction Inst;
+ Inst.Label = emitFPOLabel();
+ Inst.Op = FPOInstruction::StackAlign;
+ Inst.RegOrOffset = Align;
+ CurFPOData->Instructions.push_back(Inst);
+ return false;
+}
+
bool X86WinCOFFTargetStreamer::emitFPOEndPrologue(SMLoc L) {
if (checkInFPOPrologue(L))
return true;
@@ -250,6 +276,8 @@ struct FPOStateMachine {
unsigned CurOffset = 0;
unsigned LocalSize = 0;
unsigned SavedRegSize = 0;
+ unsigned StackOffsetBeforeAlign = 0;
+ unsigned StackAlign = 0;
unsigned Flags = 0; // FIXME: Set HasSEH / HasEH.
SmallString<128> FrameFunc;
@@ -291,24 +319,39 @@ void FPOStateMachine::emitFrameDataRecord(MCStreamer &OS, MCSymbol *Label) {
FrameFunc.clear();
raw_svector_ostream FuncOS(FrameFunc);
const MCRegisterInfo *MRI = OS.getContext().getRegisterInfo();
+ assert((StackAlign == 0 || FrameReg != 0) &&
+ "cannot align stack without frame reg");
+ StringRef CFAVar = StackAlign == 0 ? "$T0" : "$T1";
+
if (FrameReg) {
// CFA is FrameReg + FrameRegOff.
- FuncOS << "$T0 " << printFPOReg(MRI, FrameReg) << " " << FrameRegOff
+ FuncOS << CFAVar << ' ' << printFPOReg(MRI, FrameReg) << ' ' << FrameRegOff
<< " + = ";
+
+ // Assign $T0, the VFRAME register, the value of ESP after it is aligned.
+ // Starting from the CFA, we subtract the size of all pushed registers, and
+ // align the result. While we don't store any CSRs in this area, $T0 is used
+ // by S_DEFRANGE_FRAMEPOINTER_REL records to find local variables.
+ if (StackAlign) {
+ FuncOS << "$T0 " << CFAVar << ' ' << StackOffsetBeforeAlign << " - "
+ << StackAlign << " @ = ";
+ }
} else {
// The address of return address is ESP + CurOffset, but we use .raSearch to
// match MSVC. This seems to ask the debugger to subtract some combination
// of LocalSize and SavedRegSize from ESP and grovel around in that memory
// to find the address of a plausible return address.
- FuncOS << "$T0 .raSearch = ";
+ FuncOS << CFAVar << " .raSearch = ";
}
// Caller's $eip should be dereferenced CFA, and $esp should be CFA plus 4.
- FuncOS << "$eip $T0 ^ = $esp $T0 4 + = ";
+ FuncOS << "$eip " << CFAVar << " ^ = ";
+ FuncOS << "$esp " << CFAVar << " 4 + = ";
// Each saved register is stored at an unchanging negative CFA offset.
for (RegSaveOffset RO : RegSaveOffsets)
- FuncOS << printFPOReg(MRI, RO.Reg) << " $T0 " << RO.Offset << " - ^ = ";
+ FuncOS << printFPOReg(MRI, RO.Reg) << ' ' << CFAVar << ' ' << RO.Offset
+ << " - ^ = ";
// Add it to the CV string table.
CodeViewContext &CVCtx = OS.getContext().getCVContext();
@@ -380,6 +423,10 @@ bool X86WinCOFFTargetStreamer::emitFPOData(const MCSymbol *ProcSym, SMLoc L) {
FSM.FrameReg = Inst.RegOrOffset;
FSM.FrameRegOff = FSM.CurOffset;
break;
+ case FPOInstruction::StackAlign:
+ FSM.StackOffsetBeforeAlign = FSM.CurOffset;
+ FSM.StackAlign = Inst.RegOrOffset;
+ break;
case FPOInstruction::StackAlloc:
FSM.CurOffset += Inst.RegOrOffset;
FSM.LocalSize += Inst.RegOrOffset;
diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp
index 2d660ac8826..1eb9fa0bc1e 100644
--- a/llvm/lib/Target/X86/X86FrameLowering.cpp
+++ b/llvm/lib/Target/X86/X86FrameLowering.cpp
@@ -1208,6 +1208,13 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF,
if (!IsWin64Prologue && !IsFunclet && TRI->needsStackRealignment(MF)) {
assert(HasFP && "There should be a frame pointer if stack is realigned.");
BuildStackAlignAND(MBB, MBBI, DL, StackPtr, MaxAlign);
+
+ if (NeedsWinCFI) {
+ HasWinCFI = true;
+ BuildMI(MBB, MBBI, DL, TII.get(X86::SEH_StackAlign))
+ .addImm(MaxAlign)
+ .setMIFlag(MachineInstr::FrameSetup);
+ }
}
// If there is an SUB32ri of ESP immediately before this instruction, merge
diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td
index 111dc28f2eb..ca354fae833 100644
--- a/llvm/lib/Target/X86/X86InstrCompiler.td
+++ b/llvm/lib/Target/X86/X86InstrCompiler.td
@@ -240,6 +240,8 @@ let isPseudo = 1, SchedRW = [WriteSystem] in {
"#SEH_SaveXMM $reg, $dst", []>;
def SEH_StackAlloc : I<0, Pseudo, (outs), (ins i32imm:$size),
"#SEH_StackAlloc $size", []>;
+ def SEH_StackAlign : I<0, Pseudo, (outs), (ins i32imm:$align),
+ "#SEH_StackAlign $align", []>;
def SEH_SetFrame : I<0, Pseudo, (outs), (ins i32imm:$reg, i32imm:$offset),
"#SEH_SetFrame $reg, $offset", []>;
def SEH_PushFrame : I<0, Pseudo, (outs), (ins i1imm:$mode),
diff --git a/llvm/lib/Target/X86/X86MCInstLower.cpp b/llvm/lib/Target/X86/X86MCInstLower.cpp
index a6251ffb2e1..acb2bc20858 100644
--- a/llvm/lib/Target/X86/X86MCInstLower.cpp
+++ b/llvm/lib/Target/X86/X86MCInstLower.cpp
@@ -1532,6 +1532,9 @@ void X86AsmPrinter::EmitSEHInstruction(const MachineInstr *MI) {
case X86::SEH_StackAlloc:
XTS->emitFPOStackAlloc(MI->getOperand(0).getImm());
break;
+ case X86::SEH_StackAlign:
+ XTS->emitFPOStackAlign(MI->getOperand(0).getImm());
+ break;
case X86::SEH_SetFrame:
assert(MI->getOperand(1).getImm() == 0 &&
".cv_fpo_setframe takes no offset");
@@ -1809,6 +1812,7 @@ void X86AsmPrinter::EmitInstruction(const MachineInstr *MI) {
case X86::SEH_SaveReg:
case X86::SEH_SaveXMM:
case X86::SEH_StackAlloc:
+ case X86::SEH_StackAlign:
case X86::SEH_SetFrame:
case X86::SEH_PushFrame:
case X86::SEH_EndPrologue:
diff --git a/llvm/test/DebugInfo/COFF/fpo-realign-alloca.ll b/llvm/test/DebugInfo/COFF/fpo-realign-alloca.ll
index 84e112200cb..6046e2f5262 100644
--- a/llvm/test/DebugInfo/COFF/fpo-realign-alloca.ll
+++ b/llvm/test/DebugInfo/COFF/fpo-realign-alloca.ll
@@ -17,9 +17,8 @@
; CHECK: .cv_fpo_setframe %ebp
; CHECK: pushl %esi
; CHECK: .cv_fpo_pushreg %esi
-; We don't seem to need to describe this AND because at this point CSRs
-; are stored relative to EBP, but it's suspicious.
; CHECK: andl $-16, %esp
+; CHECK: .cv_fpo_stackalign 16
; CHECK: subl $32, %esp
; CHECK: .cv_fpo_stackalloc 32
; CHECK: .cv_fpo_endprologue
diff --git a/llvm/test/DebugInfo/COFF/fpo-realign-vframe.ll b/llvm/test/DebugInfo/COFF/fpo-realign-vframe.ll
new file mode 100644
index 00000000000..fded804a531
--- /dev/null
+++ b/llvm/test/DebugInfo/COFF/fpo-realign-vframe.ll
@@ -0,0 +1,230 @@
+; RUN: llc < %s | FileCheck %s --check-prefix=ASM
+; RUN: llc -filetype=obj < %s | llvm-readobj -codeview | FileCheck %s --check-prefix=OBJ
+
+; PR38857
+
+; When stack realignment is required by dynamic allocas are not used, the
+; compiler will address locals with the ESP register. However, if call argument
+; set up uses PUSH instructions, ESP may vary over the course of the function.
+; This means it's not useful as a base register for describing the locations of
+; variables. Instead, our CodeView output prefers to use the VFRAME virtual
+; register, which is defined in the FPO data as $T0. Make sure we define it.
+
+; Original C++ test case, which uses __thiscall to encourage PUSH conversion:
+; struct Foo {
+; int x = 42;
+; int __declspec(noinline) foo();
+; void __declspec(noinline) bar(int *a, int *b, double *c);
+; };
+; int Foo::foo() {
+; int a = 1;
+; int b = 2;
+; double __declspec(align(8)) force_alignment = 0.42;
+; bar(&a, &b, &force_alignment);
+; x += (int)force_alignment;
+; return x;
+; }
+; void Foo::bar(int *a, int *b, double *c) {
+; __debugbreak();
+; *c += *a + *b;
+; }
+; int main() {
+; Foo o;
+; o.foo();
+; }
+; This stops the debugger in bar, and locals in Foo::foo would be corrupt.
+
+; More reduced C code to generate this IR:
+; int getval(void);
+; void usevals(int *, int *, double *);
+; int realign_with_csrs(int x) {
+; int a = getval();
+; double __declspec(align(8)) force_alignment = 0.42;
+; usevals(&a, &x, &force_alignment);
+; return x;
+; }
+
+; Match the prologue for the .cv_fpo* directives.
+; ASM-LABEL: _realign_with_csrs:
+; ASM: .cv_fpo_proc _realign_with_csrs 4
+; ASM: # %bb.0: # %entry
+; ASM: pushl %ebp
+; ASM: .cv_fpo_pushreg %ebp
+; ASM: movl %esp, %ebp
+; ASM: .cv_fpo_setframe %ebp
+; ASM: andl $-8, %esp
+; ASM: .cv_fpo_stackalign 8
+; ASM: subl $16, %esp
+; ASM: .cv_fpo_stackalloc 16
+; ASM: .cv_fpo_endprologue
+
+; 'x' should be EBP-relative, 'a' and 'force_alignment' ESP relative.
+; ASM: calll _getval
+; ASM-DAG: leal 8(%esp), %[[LEA_DBL:[^ ]*]]
+; ASM-DAG: leal 8(%ebp), %[[LEA_X:[^ ]*]]
+; ASM-DAG: leal 4(%esp), %[[LEA_A:[^ ]*]]
+; ASM: pushl %[[LEA_DBL]]
+; ASM: pushl %[[LEA_X]]
+; ASM: pushl %[[LEA_A]]
+; ASM: calll _usevals
+; ASM: addl $12, %esp
+
+; OBJ: Subsection [
+; OBJ: SubSectionType: Symbols (0xF1)
+; OBJ: ]
+; OBJ: Subsection [
+; OBJ: SubSectionType: FrameData (0xF5)
+; Really, the only important FrameFunc is the last one.
+; OBJ: FrameData {
+; OBJ: }
+; OBJ: FrameData {
+; OBJ: }
+; OBJ: FrameData {
+; OBJ: }
+; OBJ: FrameData {
+; OBJ: FrameFunc [
+; OBJ: $T1 $ebp 4 + =
+; OBJ: $T0 $T1 4 - 8 @ =
+; OBJ: $eip $T1 ^ =
+; OBJ: $esp $T1 4 + =
+; OBJ: $ebp $T1 4 - ^ =
+; OBJ: ]
+; OBJ: }
+; OBJ: ]
+; OBJ: Subsection [
+; OBJ: SubSectionType: Symbols (0xF1)
+; OBJ: GlobalProcIdSym {
+; OBJ: Kind: S_GPROC32_ID (0x1147)
+; OBJ: DisplayName: realign_with_csrs
+; OBJ: LinkageName: _realign_with_csrs
+; OBJ: }
+; The frame register for locals should be VFRAME, and EBP for parameters.
+; OBJ: FrameProcSym {
+; OBJ: Kind: S_FRAMEPROC (0x1012)
+; OBJ: TotalFrameBytes: 0x14
+; OBJ: LocalFramePtrReg: VFRAME (0x7536)
+; OBJ: ParamFramePtrReg: EBP (0x16)
+; OBJ: }
+; As seen in ASM, offset of x is 8.
+; OBJ: LocalSym {
+; OBJ: Kind: S_LOCAL (0x113E)
+; OBJ: Type: int (0x74)
+; OBJ: Flags [ (0x1)
+; OBJ: IsParameter (0x1)
+; OBJ: ]
+; OBJ: VarName: x
+; OBJ: }
+; OBJ: DefRangeFramePointerRelSym {
+; OBJ: Kind: S_DEFRANGE_FRAMEPOINTER_REL (0x1142)
+; OBJ: Offset: 8
+; OBJ: }
+; ESP is VFRAME - 16, ESP offset of 'a' is 4, so -12.
+; OBJ: LocalSym {
+; OBJ: Kind: S_LOCAL (0x113E)
+; OBJ: Type: int (0x74)
+; OBJ: Flags [ (0x0)
+; OBJ: ]
+; OBJ: VarName: a
+; OBJ: }
+; OBJ: DefRangeFramePointerRelSym {
+; OBJ: Kind: S_DEFRANGE_FRAMEPOINTER_REL (0x1142)
+; OBJ: Offset: -12
+; OBJ: }
+; ESP is VFRAME - 16, ESP offset of 'force_alignment' is 8, so -8.
+; OBJ: LocalSym {
+; OBJ: Kind: S_LOCAL (0x113E)
+; OBJ: Type: double (0x41)
+; OBJ: Flags [ (0x0)
+; OBJ: ]
+; OBJ: VarName: force_alignment
+; OBJ: }
+; OBJ: DefRangeFramePointerRelSym {
+; OBJ: Kind: S_DEFRANGE_FRAMEPOINTER_REL (0x1142)
+; OBJ: Offset: -8
+; OBJ: }
+; OBJ: ProcEnd {
+; OBJ: Kind: S_PROC_ID_END (0x114F)
+; OBJ: }
+; OBJ: ]
+
+; ModuleID = 't.c'
+source_filename = "t.c"
+target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
+target triple = "i386-pc-windows-msvc19.14.26433"
+
+; Function Attrs: nounwind
+define dso_local i32 @realign_with_csrs(i32 %x) local_unnamed_addr #0 !dbg !8 {
+entry:
+ %x.addr = alloca i32, align 4
+ %a = alloca i32, align 4
+ %force_alignment = alloca double, align 8
+ store i32 %x, i32* %x.addr, align 4, !tbaa !17
+ call void @llvm.dbg.declare(metadata i32* %x.addr, metadata !13, metadata !DIExpression()), !dbg !21
+ %0 = bitcast i32* %a to i8*, !dbg !22
+ call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0) #4, !dbg !22
+ call void @llvm.dbg.declare(metadata i32* %a, metadata !14, metadata !DIExpression()), !dbg !22
+ %call = tail call i32 @getval() #4, !dbg !22
+ store i32 %call, i32* %a, align 4, !dbg !22, !tbaa !17
+ %1 = bitcast double* %force_alignment to i8*, !dbg !23
+ call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %1) #4, !dbg !23
+ call void @llvm.dbg.declare(metadata double* %force_alignment, metadata !15, metadata !DIExpression()), !dbg !23
+ store double 4.200000e-01, double* %force_alignment, align 8, !dbg !23, !tbaa !24
+ call void @usevals(i32* nonnull %a, i32* nonnull %x.addr, double* nonnull %force_alignment) #4, !dbg !26
+ %2 = load i32, i32* %x.addr, align 4, !dbg !27, !tbaa !17
+ call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %1) #4, !dbg !28
+ call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0) #4, !dbg !28
+ ret i32 %2, !dbg !27
+}
+
+; Function Attrs: nounwind readnone speculatable
+declare void @llvm.dbg.declare(metadata, metadata, metadata) #1
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #2
+
+declare dso_local i32 @getval() local_unnamed_addr #3
+
+declare dso_local void @usevals(i32*, i32*, double*) local_unnamed_addr #3
+
+; Function Attrs: argmemonly nounwind
+declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #2
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone speculatable }
+attributes #2 = { argmemonly nounwind }
+attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #4 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!3, !4, !5, !6}
+!llvm.ident = !{!7}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 8.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "t.c", directory: "C:\5Csrc\5Cllvm-project\5Cbuild", checksumkind: CSK_MD5, checksum: "a646950309d5d01d8087fc10fea33941")
+!2 = !{}
+!3 = !{i32 1, !"NumRegisterParameters", i32 0}
+!4 = !{i32 2, !"CodeView", i32 1}
+!5 = !{i32 2, !"Debug Info Version", i32 3}
+!6 = !{i32 1, !"wchar_size", i32 2}
+!7 = !{!"clang version 8.0.0 "}
+!8 = distinct !DISubprogram(name: "realign_with_csrs", scope: !1, file: !1, line: 3, type: !9, isLocal: false, isDefinition: true, scopeLine: 3, flags: DIFlagPrototyped, isOptimized: true, unit: !0, retainedNodes: !12)
+!9 = !DISubroutineType(types: !10)
+!10 = !{!11, !11}
+!11 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!12 = !{!13, !14, !15}
+!13 = !DILocalVariable(name: "x", arg: 1, scope: !8, file: !1, line: 3, type: !11)
+!14 = !DILocalVariable(name: "a", scope: !8, file: !1, line: 4, type: !11)
+!15 = !DILocalVariable(name: "force_alignment", scope: !8, file: !1, line: 5, type: !16, align: 64)
+!16 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float)
+!17 = !{!18, !18, i64 0}
+!18 = !{!"int", !19, i64 0}
+!19 = !{!"omnipotent char", !20, i64 0}
+!20 = !{!"Simple C/C++ TBAA"}
+!21 = !DILocation(line: 3, scope: !8)
+!22 = !DILocation(line: 4, scope: !8)
+!23 = !DILocation(line: 5, scope: !8)
+!24 = !{!25, !25, i64 0}
+!25 = !{!"double", !19, i64 0}
+!26 = !DILocation(line: 6, scope: !8)
+!27 = !DILocation(line: 7, scope: !8)
+!28 = !DILocation(line: 8, scope: !8)
diff --git a/llvm/test/MC/COFF/cv-fpo-realign.s b/llvm/test/MC/COFF/cv-fpo-realign.s
new file mode 100644
index 00000000000..247cad9334b
--- /dev/null
+++ b/llvm/test/MC/COFF/cv-fpo-realign.s
@@ -0,0 +1,199 @@
+# RUN: llvm-mc -triple=i686-windows-msvc -filetype=obj < %s | llvm-readobj -codeview | FileCheck %s
+
+# Test for .cv_fpo_stackalign. We should generate FPO data that restores CSRs
+# at each instruction, and in the last FrameData we should use the '@'
+# alignment operator to define $T0, the vframe value.
+
+# Based on this C code:
+# void usevals(int, int, double*);
+# int realign_with_csrs() {
+# int a = getval();
+# int b = getval();
+# double __declspec(align(8)) force_alignment = 0.42;
+# usevals(a, b, &force_alignment);
+# return a + b;
+# }
+
+# CHECK: Subsection [
+# CHECK: SubSectionType: Symbols (0xF1)
+# CHECK: Compile3Sym {
+# CHECK: Kind: S_COMPILE3 (0x113C)
+# CHECK: }
+# CHECK: ]
+# CHECK: Subsection [
+# CHECK: SubSectionType: FrameData (0xF5)
+# CHECK: FrameData {
+# CHECK: FrameFunc [
+# CHECK: $T0 .raSearch =
+# CHECK: $eip $T0 ^ =
+# CHECK: $esp $T0 4 + =
+# CHECK: ]
+# CHECK: }
+# CHECK: FrameData {
+# CHECK: FrameFunc [
+# CHECK: $T0 .raSearch =
+# CHECK: $eip $T0 ^ =
+# CHECK: $esp $T0 4 + =
+# CHECK: $ebp $T0 4 - ^ =
+# CHECK: ]
+# CHECK: }
+# CHECK: FrameData {
+# CHECK: FrameFunc [
+# CHECK: $T0 $ebp 4 + =
+# CHECK: $eip $T0 ^ =
+# CHECK: $esp $T0 4 + =
+# CHECK: $ebp $T0 4 - ^ =
+# CHECK: ]
+# CHECK: }
+# CHECK: FrameData {
+# CHECK: FrameFunc [
+# CHECK: $T0 $ebp 4 + =
+# CHECK: $eip $T0 ^ =
+# CHECK: $esp $T0 4 + =
+# CHECK: $ebp $T0 4 - ^ =
+# CHECK: $edi $T0 8 - ^ =
+# CHECK: ]
+# CHECK: }
+# CHECK: FrameData {
+# CHECK: FrameFunc [
+# CHECK: $T0 $ebp 4 + =
+# CHECK: $eip $T0 ^ =
+# CHECK: $esp $T0 4 + =
+# CHECK: $ebp $T0 4 - ^ =
+# CHECK: $edi $T0 8 - ^ =
+# CHECK: $esi $T0 12 - ^ =
+# CHECK: ]
+# CHECK: }
+# CHECK: FrameData {
+# CHECK: FrameFunc [
+# CHECK: $T1 $ebp 4 + =
+# CHECK: $T0 $T1 12 - 8 @ =
+# CHECK: $eip $T1 ^ =
+# CHECK: $esp $T1 4 + =
+# CHECK: $ebp $T1 4 - ^ =
+# CHECK: $edi $T1 8 - ^ =
+# CHECK: $esi $T1 12 - ^ =
+# CHECK: ]
+# CHECK: }
+# CHECK: ]
+# CHECK: Subsection [
+# CHECK: SubSectionType: Symbols (0xF1)
+# CHECK: ]
+# CHECK: Subsection [
+# CHECK: SubSectionType: FileChecksums (0xF4)
+# CHECK: ]
+# CHECK: Subsection [
+# CHECK: SubSectionType: StringTable (0xF3)
+# CHECK: ]
+
+ .text
+ .def _realign_with_csrs; .scl 2; .type 32; .endef
+ .globl _realign_with_csrs # -- Begin function realign_with_csrs
+_realign_with_csrs: # @realign_with_csrs
+Lfunc_begin0:
+ .cv_func_id 0
+ .cv_file 1 "C:\\src\\llvm-project\\build\\t.c" "2A4F9B6BBBF7845521201755D1B14ACC" 1
+ .cv_loc 0 1 4 0 # t.c:4:0
+ .cv_fpo_proc _realign_with_csrs 0
+# %bb.0: # %entry
+ pushl %ebp
+ .cv_fpo_pushreg %ebp
+ movl %esp, %ebp
+ .cv_fpo_setframe %ebp
+Ltmp0:
+ pushl %edi
+ .cv_fpo_pushreg %edi
+ pushl %esi
+ .cv_fpo_pushreg %esi
+ andl $-8, %esp
+ .cv_fpo_stackalign 8
+ subl $8, %esp
+ .cv_fpo_stackalloc 8
+ .cv_fpo_endprologue
+ .cv_loc 0 1 5 0 # t.c:5:0
+ calll _getval
+ movl %eax, %esi
+ .cv_loc 0 1 6 0 # t.c:6:0
+ calll _getval
+ movl %eax, %edi
+ movl %esp, %eax
+ .cv_loc 0 1 7 0 # t.c:7:0
+ movl $1071309127, 4(%esp) # imm = 0x3FDAE147
+ movl $-1374389535, (%esp) # imm = 0xAE147AE1
+ .cv_loc 0 1 8 0 # t.c:8:0
+ pushl %eax
+ pushl %edi
+ pushl %esi
+ calll _usevals
+ addl $12, %esp
+ .cv_loc 0 1 9 0 # t.c:9:0
+ addl %esi, %edi
+ movl %edi, %eax
+ leal -8(%ebp), %esp
+ popl %esi
+ popl %edi
+ popl %ebp
+ retl
+Ltmp1:
+ .cv_fpo_endproc
+Lfunc_end0:
+ # -- End function
+ .section .debug$S,"dr"
+ .p2align 2
+ .long 4 # Debug section magic
+ .long 241
+ .long Ltmp3-Ltmp2 # Subsection size
+Ltmp2:
+ .short Ltmp5-Ltmp4 # Record length
+Ltmp4:
+ .short 4412 # Record kind: S_COMPILE3
+ .long 0 # Flags and language
+ .short 7 # CPUType
+ .short 8 # Frontend version
+ .short 0
+ .short 0
+ .short 0
+ .short 8000 # Backend version
+ .short 0
+ .short 0
+ .short 0
+ .asciz "clang version 8.0.0 " # Null-terminated compiler version string
+Ltmp5:
+Ltmp3:
+ .p2align 2
+ .cv_fpo_data _realign_with_csrs
+ .long 241 # Symbol subsection for realign_with_csrs
+ .long Ltmp7-Ltmp6 # Subsection size
+Ltmp6:
+ .short Ltmp9-Ltmp8 # Record length
+Ltmp8:
+ .short 4423 # Record kind: S_GPROC32_ID
+ .long 0 # PtrParent
+ .long 0 # PtrEnd
+ .long 0 # PtrNext
+ .long Lfunc_end0-_realign_with_csrs # Code size
+ .long 0 # Offset after prologue
+ .long 0 # Offset before epilogue
+ .long 0 # Function type index
+ .secrel32 _realign_with_csrs # Function section relative address
+ .secidx _realign_with_csrs # Function section index
+ .byte 0 # Flags
+ .asciz "realign_with_csrs" # Function name
+Ltmp9:
+ .short Ltmp11-Ltmp10 # Record length
+Ltmp10:
+ .short 4114 # Record kind: S_FRAMEPROC
+ .long 12 # FrameSize
+ .long 0 # Padding
+ .long 0 # Offset of padding
+ .long 8 # Bytes of callee saved registers
+ .long 0 # Exception handler offset
+ .short 0 # Exception handler section
+ .long 1196032 # Flags (defines frame register)
+Ltmp11:
+ .short 2 # Record length
+ .short 4431 # Record kind: S_PROC_ID_END
+Ltmp7:
+ .p2align 2
+ .cv_filechecksums # File index to string table offset subsection
+ .cv_stringtable # String table
OpenPOWER on IntegriCloud