diff options
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.cpp | 14 | ||||
-rw-r--r-- | llvm/lib/CodeGen/AsmPrinter/CodeViewDebug.h | 2 | ||||
-rw-r--r-- | llvm/lib/Target/X86/AsmParser/X86AsmParser.cpp | 13 | ||||
-rw-r--r-- | llvm/lib/Target/X86/MCTargetDesc/X86TargetStreamer.h | 1 | ||||
-rw-r--r-- | llvm/lib/Target/X86/MCTargetDesc/X86WinCOFFTargetStreamer.cpp | 55 | ||||
-rw-r--r-- | llvm/lib/Target/X86/X86FrameLowering.cpp | 7 | ||||
-rw-r--r-- | llvm/lib/Target/X86/X86InstrCompiler.td | 2 | ||||
-rw-r--r-- | llvm/lib/Target/X86/X86MCInstLower.cpp | 4 | ||||
-rw-r--r-- | llvm/test/DebugInfo/COFF/fpo-realign-alloca.ll | 3 | ||||
-rw-r--r-- | llvm/test/DebugInfo/COFF/fpo-realign-vframe.ll | 230 | ||||
-rw-r--r-- | llvm/test/MC/COFF/cv-fpo-realign.s | 199 |
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 |