From 54682d871d9e2d8f19ca58ce47a9673401d011ff Mon Sep 17 00:00:00 2001 From: David Stenberg Date: Thu, 5 Dec 2019 10:49:41 +0100 Subject: [PATCH 1/9] [DebugInfo] Handle call site values for instructions before call bundle Summary: If a call is bundled then the code that looks for instructions that produce parameter values would break when reaching the call's bundle header, due to the `ifCall(/*AnyInBundle*/)` invocation returning true. It is not enough to simply ignore bundle headers in the `isCall()` invocation, as the bundle header may have defines of parameter registers due to the call, meaning that such registers would incorrectly be removed from the worklist. Therefore, do not look at bundle headers at all. Reviewers: djtodoro, NikolaPrica, aprantl, vsk Reviewed By: aprantl, vsk Subscribers: hiraditya, llvm-commits Tags: #debug-info, #llvm Differential Revision: https://reviews.llvm.org/D71024 --- llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp | 4 + ...dbgcall-site-instr-before-bundled-call.mir | 187 ++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 llvm/test/DebugInfo/MIR/Hexagon/dbgcall-site-instr-before-bundled-call.mir diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp index bddb8bf3532139..09772537a97b8f 100644 --- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp @@ -617,6 +617,10 @@ static void collectCallSiteParameters(const MachineInstr *CallMI, // Search for a loading value in forwarding registers. for (; I != MBB->rend(); ++I) { + // Skip bundle headers. + if (I->isBundle()) + continue; + // If the next instruction is a call we can not interpret parameter's // forwarding registers or we finished the interpretation of all parameters. if (I->isCall()) diff --git a/llvm/test/DebugInfo/MIR/Hexagon/dbgcall-site-instr-before-bundled-call.mir b/llvm/test/DebugInfo/MIR/Hexagon/dbgcall-site-instr-before-bundled-call.mir new file mode 100644 index 00000000000000..8ae628af2c0994 --- /dev/null +++ b/llvm/test/DebugInfo/MIR/Hexagon/dbgcall-site-instr-before-bundled-call.mir @@ -0,0 +1,187 @@ +# RUN: llc -mtriple hexagon -debug-entry-values -start-after=machineverifier -filetype=obj %s -o - | llvm-dwarfdump - | FileCheck %s + +# Based on the following C reproducer: +# +# int ga, gb, gc; +# +# extern void callee(int, int, int); +# +# void caller() { +# int a = ga; +# int b = gb; +# int c = gc; +# +# // Clobber all integer registers. +# __asm("" : : : +# "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", +# "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", +# "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28"); +# +# callee(a, b, c); +# } + +--- | + target datalayout = "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32:32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048:2048:2048" + target triple = "hexagon" + + @ga = common global i32 0, align 4 + @gb = common global i32 0, align 4 + @gc = common global i32 0, align 4 + + ; Function Attrs: nounwind + define void @caller() #0 !dbg !12 { + entry: + %0 = load i32, i32* @ga, align 4, !dbg !15 + %1 = load i32, i32* @gb, align 4, !dbg !16 + %2 = load i32, i32* @gc, align 4, !dbg !17 + call void asm sideeffect "", "~{r0},~{r1},~{r2},~{r3},~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11},~{r12},~{r13},~{r14},~{r15},~{r16},~{r17},~{r18},~{r19},~{r20},~{r21},~{r22},~{r23},~{r24},~{r25},~{r26},~{r27},~{r28}"(), !dbg !18, !srcloc !19 + call void @callee(i32 %0, i32 %1, i32 %2), !dbg !20 + ret void, !dbg !21 + } + + declare !dbg !4 void @callee(i32, i32, i32) + + attributes #0 = { nounwind } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!8, !9, !10} + !llvm.ident = !{!11} + + !0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !3, globals: !2, nameTableKind: None) + !1 = !DIFile(filename: "h.c", directory: "/") + !2 = !{} + !3 = !{!4} + !4 = !DISubprogram(name: "callee", scope: !1, file: !1, line: 3, type: !5, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized, retainedNodes: !2) + !5 = !DISubroutineType(types: !6) + !6 = !{null, !7, !7, !7} + !7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !8 = !{i32 7, !"Dwarf Version", i32 4} + !9 = !{i32 2, !"Debug Info Version", i32 3} + !10 = !{i32 1, !"wchar_size", i32 4} + !11 = !{!"clang version 10.0.0"} + !12 = distinct !DISubprogram(name: "caller", scope: !1, file: !1, line: 5, type: !13, scopeLine: 5, flags: DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !2) + !13 = !DISubroutineType(types: !14) + !14 = !{null} + !15 = !DILocation(line: 6, scope: !12) + !16 = !DILocation(line: 7, scope: !12) + !17 = !DILocation(line: 8, scope: !12) + !18 = !DILocation(line: 11, scope: !12) + !19 = !{i32 158} + !20 = !DILocation(line: 16, scope: !12) + !21 = !DILocation(line: 17, scope: !12) + +... +--- +name: caller +tracksRegLiveness: true +frameInfo: + stackSize: 64 + maxAlignment: 4 + adjustsStack: true + hasCalls: true + maxCallFrameSize: 0 +fixedStack: + - { id: 0, type: spill-slot, offset: -48, size: 8, alignment: 8, callee-saved-register: '$d13' } + - { id: 1, type: spill-slot, offset: -40, size: 8, alignment: 8, callee-saved-register: '$d12' } + - { id: 2, type: spill-slot, offset: -32, size: 8, alignment: 8, callee-saved-register: '$d11' } + - { id: 3, type: spill-slot, offset: -24, size: 8, alignment: 8, callee-saved-register: '$d10' } + - { id: 4, type: spill-slot, offset: -16, size: 8, alignment: 8, callee-saved-register: '$d9' } + - { id: 5, type: spill-slot, offset: -8, size: 8, alignment: 8, callee-saved-register: '$d8' } +stack: + - { id: 0, type: spill-slot, offset: -52, size: 4, alignment: 4 } + - { id: 1, type: spill-slot, offset: -56, size: 4, alignment: 4 } + - { id: 2, type: spill-slot, offset: -60, size: 4, alignment: 4 } +callSites: + - { bb: 0, offset: 40, fwdArgRegs: + - { arg: 0, reg: '$r0' } + - { arg: 1, reg: '$r1' } + - { arg: 2, reg: '$r2' } } +body: | + bb.0.entry: + liveins: $d8, $d9, $d10, $d11, $d12, $d13, $d8, $d9, $d10, $d11, $d12, $d13 + + BUNDLE implicit-def $r29, implicit-def $r30, implicit $r29, implicit killed $framekey, implicit killed $framelimit, implicit killed $r30, implicit killed $r31, implicit killed $d8, debug-location !15 { + $r29 = S2_allocframe $r29, 64, implicit-def $r30, implicit killed $framekey, implicit killed $framelimit, implicit killed $r30, implicit killed $r31, debug-location !15 :: (store 4 into stack) + S2_storerd_io internal $r29, -16, killed $d8, debug-location !15 :: (store 8 into %fixed-stack.5) + } + CFI_INSTRUCTION def_cfa $r30, 8 + CFI_INSTRUCTION offset $r31, -4 + CFI_INSTRUCTION offset $r30, -8 + CFI_INSTRUCTION offset $r17, -12 + CFI_INSTRUCTION offset $r16, -16 + CFI_INSTRUCTION offset $r19, -20 + CFI_INSTRUCTION offset $r18, -24 + CFI_INSTRUCTION offset $r21, -28 + CFI_INSTRUCTION offset $r20, -32 + CFI_INSTRUCTION offset $r23, -36 + CFI_INSTRUCTION offset $r22, -40 + CFI_INSTRUCTION offset $r25, -44 + CFI_INSTRUCTION offset $r24, -48 + CFI_INSTRUCTION offset $r27, -52 + CFI_INSTRUCTION offset $r26, -56 + BUNDLE implicit $r29, implicit killed $d9, implicit killed $d10, debug-location !15 { + S2_storerd_io $r29, 48, killed $d9, debug-location !15 :: (store 8 into %fixed-stack.4) + S2_storerd_io $r29, 40, killed $d10, debug-location !15 :: (store 8 into %fixed-stack.3) + } + BUNDLE implicit $r29, implicit killed $d11, implicit killed $d12, debug-location !15 { + S2_storerd_io $r29, 32, killed $d11, debug-location !15 :: (store 8 into %fixed-stack.2) + S2_storerd_io $r29, 24, killed $d12, debug-location !15 :: (store 8 into %fixed-stack.1) + } + BUNDLE implicit-def $r0, implicit $r29, implicit killed $d13, implicit $gp, debug-location !15 { + S2_storerd_io $r29, 16, killed $d13, debug-location !15 :: (store 8 into %fixed-stack.0) + renamable $r0 = L2_loadrigp @ga, implicit $gp, debug-location !15 :: (dereferenceable load 4 from @ga) + } + BUNDLE implicit-def $r0, implicit $r29, implicit killed $r0, implicit $gp, debug-location !16 { + S2_storeri_io $r29, 12, killed renamable $r0, debug-location !16 :: (store 4 into %stack.0) + renamable $r0 = L2_loadrigp @gb, implicit $gp, debug-location !16 :: (dereferenceable load 4 from @gb) + } + BUNDLE implicit-def $r0, implicit $r29, implicit killed $r0, implicit killed $gp, debug-location !17 { + S2_storeri_io $r29, 8, killed renamable $r0, debug-location !17 :: (store 4 into %stack.1) + renamable $r0 = L2_loadrigp @gc, implicit killed $gp, debug-location !17 :: (dereferenceable load 4 from @gc) + } + S2_storeri_io $r29, 4, killed renamable $r0, debug-location !18 :: (store 4 into %stack.2) + INLINEASM &"", 1, 12, implicit-def dead early-clobber $r0, 12, implicit-def dead early-clobber $r1, 12, implicit-def dead early-clobber $r2, 12, implicit-def dead early-clobber $r3, 12, implicit-def dead early-clobber $r4, 12, implicit-def dead early-clobber $r5, 12, implicit-def dead early-clobber $r6, 12, implicit-def dead early-clobber $r7, 12, implicit-def dead early-clobber $r8, 12, implicit-def dead early-clobber $r9, 12, implicit-def dead early-clobber $r10, 12, implicit-def dead early-clobber $r11, 12, implicit-def dead early-clobber $r12, 12, implicit-def dead early-clobber $r13, 12, implicit-def dead early-clobber $r14, 12, implicit-def dead early-clobber $r15, 12, implicit-def dead early-clobber $r16, 12, implicit-def dead early-clobber $r17, 12, implicit-def dead early-clobber $r18, 12, implicit-def dead early-clobber $r19, 12, implicit-def dead early-clobber $r20, 12, implicit-def dead early-clobber $r21, 12, implicit-def dead early-clobber $r22, 12, implicit-def dead early-clobber $r23, 12, implicit-def dead early-clobber $r24, 12, implicit-def dead early-clobber $r25, 12, implicit-def dead early-clobber $r26, 12, implicit-def dead early-clobber $r27, 12, implicit-def dead early-clobber $r28, !19, debug-location !18 + BUNDLE implicit-def $r0, implicit-def $r1, implicit $r29, debug-location !20 { + $r0 = L2_loadri_io $r29, 12, debug-location !20 :: (load 4 from %stack.0) + $r1 = L2_loadri_io $r29, 8, debug-location !20 :: (load 4 from %stack.1) + } + BUNDLE implicit-def dead $r2, implicit-def dead $pc, implicit-def dead $r31, implicit-def $r29, implicit $r29, implicit killed $r0, implicit killed $r1, debug-location !20 { + $r2 = L2_loadri_io $r29, 4, debug-location !20 :: (load 4 from %stack.2) + J2_call @callee, hexagoncsr, implicit-def dead $pc, implicit-def dead $r31, implicit $r29, implicit killed $r0, implicit killed $r1, implicit internal killed $r2, implicit-def $r29, debug-location !20 + } + BUNDLE implicit-def $d8, implicit-def $r16, implicit-def $r17, implicit-def $d9, implicit-def $r18, implicit-def $r19, implicit $r29, debug-location !21 { + $d8 = L2_loadrd_io $r29, 56, debug-location !21 :: (load 8 from %fixed-stack.5) + $d9 = L2_loadrd_io $r29, 48, debug-location !21 :: (load 8 from %fixed-stack.4) + } + BUNDLE implicit-def $d10, implicit-def $r20, implicit-def $r21, implicit-def $d11, implicit-def $r22, implicit-def $r23, implicit $r29, debug-location !21 { + $d10 = L2_loadrd_io $r29, 40, debug-location !21 :: (load 8 from %fixed-stack.3) + $d11 = L2_loadrd_io $r29, 32, debug-location !21 :: (load 8 from %fixed-stack.2) + } + BUNDLE implicit-def $d12, implicit-def $r24, implicit-def $r25, implicit-def $d13, implicit-def $r26, implicit-def $r27, implicit killed $r29, debug-location !21 { + $d12 = L2_loadrd_io $r29, 24, debug-location !21 :: (load 8 from %fixed-stack.1) + $d13 = L2_loadrd_io killed $r29, 16, debug-location !21 :: (load 8 from %fixed-stack.0) + } + $d15 = L4_return killed $r30, implicit-def $pc, implicit-def $r29, implicit killed $framekey, implicit-def dead $pc, implicit $d8, implicit $d9, implicit $d10, implicit $d11, implicit $d12, implicit $d13, debug-location !21 + +... + +# Verify that call site entries are emitted for all three parameters. +# Previously the code that's looking for instructions to describe parameters +# with would stop when reaching the bundle header for the bundled call, +# resulting in $r0 and $r1 not being described. +# +# Please note that at the time of creating this test the Hexagon target did not +# support call site information, so the "callSites" array has been manually +# added. + +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg2 R2) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg29 R29+4, DW_OP_deref_size 0x4) + +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg1 R1) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg29 R29+8, DW_OP_deref_size 0x4) + +# CHECK: DW_TAG_GNU_call_site_parameter +# CHECK-NEXT: DW_AT_location (DW_OP_reg0 R0) +# CHECK-NEXT: DW_AT_GNU_call_site_value (DW_OP_breg29 R29+12, DW_OP_deref_size 0x4) From 158356b82b8a2fa8f1cb12a3735274f6a6c3c5f2 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Thu, 5 Dec 2019 12:08:31 +0100 Subject: [PATCH 2/9] [clangd] More unittests for cross-file rename. Summary: The previous unittests for cross-file rename was kind of weak. With this patch, we should have more test coverage, and it is easy to add more tests in the future. Reviewers: ilya-biryukov, kbobyrev Reviewed By: ilya-biryukov Subscribers: merge_guards_bot, MaskRay, jkorous, arphaman, kadircet, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D71050 --- .../clangd/unittests/RenameTests.cpp | 153 +++++++++++++++++- .../clangd/unittests/SyncAPI.cpp | 2 +- 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp index 09146f52e5e0f4..8a54b552258c14 100644 --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -7,6 +7,8 @@ //===----------------------------------------------------------------------===// #include "Annotations.h" +#include "ClangdServer.h" +#include "SyncAPI.h" #include "TestFS.h" #include "TestTU.h" #include "index/Ref.h" @@ -575,7 +577,7 @@ TEST(RenameTest, MainFileReferencesOnly) { expectedResult(Code, NewName)); } -TEST(RenameTests, CrossFile) { +TEST(CrossFileRenameTests, DirtyBuffer) { Annotations FooCode("class [[Foo]] {};"); std::string FooPath = testPath("foo.cc"); Annotations FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer"); @@ -658,6 +660,155 @@ TEST(RenameTests, CrossFile) { testing::HasSubstr("too many occurrences")); } +TEST(CrossFileRenameTests, WithUpToDateIndex) { + MockCompilationDatabase CDB; + CDB.ExtraClangFlags = {"-xc++"}; + class IgnoreDiagnostics : public DiagnosticsConsumer { + void onDiagnosticsReady(PathRef File, + std::vector Diagnostics) override {} + } DiagConsumer; + // rename is runnning on the "^" point in FooH, and "[[]]" ranges are the + // expcted rename occurrences. + struct Case { + llvm::StringRef FooH; + llvm::StringRef FooCC; + } Cases [] = { + { + // classes. + R"cpp( + class [[Fo^o]] { + [[Foo]](); + ~[[Foo]](); + }; + )cpp", + R"cpp( + #include "foo.h" + [[Foo]]::[[Foo]]() {} + [[Foo]]::~[[Foo]]() {} + + void func() { + [[Foo]] foo; + } + )cpp", + }, + { + // class methods. + R"cpp( + class Foo { + void [[f^oo]](); + }; + )cpp", + R"cpp( + #include "foo.h" + void Foo::[[foo]]() {} + + void func(Foo* p) { + p->[[foo]](); + } + )cpp", + }, + { + // functions. + R"cpp( + void [[f^oo]](); + )cpp", + R"cpp( + #include "foo.h" + void [[foo]]() {} + + void func() { + [[foo]](); + } + )cpp", + }, + { + // typedefs. + R"cpp( + typedef int [[IN^T]]; + [[INT]] foo(); + )cpp", + R"cpp( + #include "foo.h" + [[INT]] foo() {} + )cpp", + }, + { + // usings. + R"cpp( + using [[I^NT]] = int; + [[INT]] foo(); + )cpp", + R"cpp( + #include "foo.h" + [[INT]] foo() {} + )cpp", + }, + { + // variables. + R"cpp( + static const int [[VA^R]] = 123; + )cpp", + R"cpp( + #include "foo.h" + int s = [[VAR]]; + )cpp", + }, + { + // scope enums. + R"cpp( + enum class [[K^ind]] { ABC }; + )cpp", + R"cpp( + #include "foo.h" + [[Kind]] ff() { + return [[Kind]]::ABC; + } + )cpp", + }, + { + // enum constants. + R"cpp( + enum class Kind { [[A^BC]] }; + )cpp", + R"cpp( + #include "foo.h" + Kind ff() { + return Kind::[[ABC]]; + } + )cpp", + }, + }; + + for (const auto& T : Cases) { + Annotations FooH(T.FooH); + Annotations FooCC(T.FooCC); + std::string FooHPath = testPath("foo.h"); + std::string FooCCPath = testPath("foo.cc"); + + MockFSProvider FS; + FS.Files[FooHPath] = FooH.code(); + FS.Files[FooCCPath] = FooCC.code(); + + auto ServerOpts = ClangdServer::optsForTest(); + ServerOpts.CrossFileRename = true; + ServerOpts.BuildDynamicSymbolIndex = true; + ClangdServer Server(CDB, FS, DiagConsumer, ServerOpts); + + // Add all files to clangd server to make sure the dynamic index has been + // built. + runAddDocument(Server, FooHPath, FooH.code()); + runAddDocument(Server, FooCCPath, FooCC.code()); + + llvm::StringRef NewName = "NewName"; + auto FileEditsList = + llvm::cantFail(runRename(Server, FooHPath, FooH.point(), NewName)); + EXPECT_THAT(applyEdits(std::move(FileEditsList)), + UnorderedElementsAre( + Pair(Eq(FooHPath), Eq(expectedResult(T.FooH, NewName))), + Pair(Eq(FooCCPath), Eq(expectedResult(T.FooCC, NewName))))); + } +} + TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) { // cross-file rename should work for function-local symbols, even there is no // index provided. diff --git a/clang-tools-extra/clangd/unittests/SyncAPI.cpp b/clang-tools-extra/clangd/unittests/SyncAPI.cpp index 085eacd42fee30..5c7949ab41baf0 100644 --- a/clang-tools-extra/clangd/unittests/SyncAPI.cpp +++ b/clang-tools-extra/clangd/unittests/SyncAPI.cpp @@ -99,7 +99,7 @@ runFindDocumentHighlights(ClangdServer &Server, PathRef File, Position Pos) { llvm::Expected runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName) { llvm::Optional> Result; - Server.rename(File, Pos, NewName, /*WantFormat=*/true, capture(Result)); + Server.rename(File, Pos, NewName, /*WantFormat=*/false, capture(Result)); return std::move(*Result); } From 52b231ee84cac576044e147e72d9bd5b290de1f3 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic Date: Thu, 5 Dec 2019 12:21:51 +0100 Subject: [PATCH 3/9] [LiveDebugValues] Silence the unused var warning; NFC --- llvm/lib/CodeGen/LiveDebugValues.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/CodeGen/LiveDebugValues.cpp b/llvm/lib/CodeGen/LiveDebugValues.cpp index 58680f7be4ea75..04efa7bc35e966 100644 --- a/llvm/lib/CodeGen/LiveDebugValues.cpp +++ b/llvm/lib/CodeGen/LiveDebugValues.cpp @@ -859,8 +859,7 @@ void LiveDebugValues::insertTransferDebugPair( unsigned NewReg) { const MachineInstr *DebugInstr = &VarLocIDs[OldVarID].MI; - auto ProcessVarLoc = [&MI, &OpenRanges, &Transfers, &DebugInstr, - &VarLocIDs](VarLoc &VL) { + auto ProcessVarLoc = [&MI, &OpenRanges, &Transfers, &VarLocIDs](VarLoc &VL) { unsigned LocId = VarLocIDs.insert(VL); // Close this variable's previous location range. From 4ee76a922aad743818d56f58630cf8da25602251 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Wed, 4 Dec 2019 14:03:18 +0100 Subject: [PATCH 4/9] [llvm/DWARF] Return section offset from DWARFUnit::get{Loc,Rng}listOffset Summary: Currently these function return the raw content of the appropriate table header, which means they are relative to the DW_AT_{loc,rng}list_base, and one has to relocate them in order to do anything. This changes the functions to perform the relocation themselves, which seems more clearer, particularly as they are sitting right next to the find{Rng,Loc}listFromOffset functions, but one *cannot* simply take the result of these functions and take pass them there. The only effect of this patch is to change what value is dumped for the DW_AT_ranges attribute, which I think is for the better, as previously the values appeared to point into thin air. (The main reason I am looking at this is because I was trying to implement equivalent functionality in lldb's DWARFUnit, and was stumped by this behavior. Reviewers: dblaikie, JDevlieghere, aprantl Subscribers: hiraditya, llvm-commits, SouraVX Tags: #llvm Differential Revision: https://reviews.llvm.org/D71006 --- llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h | 12 ++++++++---- llvm/lib/DebugInfo/DWARF/DWARFDie.cpp | 2 +- llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp | 2 +- llvm/test/DebugInfo/X86/dwarfdump-rnglists-dwarf64.s | 4 ++-- llvm/test/DebugInfo/X86/dwarfdump-rnglists.s | 4 ++-- llvm/test/DebugInfo/X86/split-dwarf-v5-ranges.ll | 2 +- llvm/test/tools/llvm-dwarfdump/X86/no_debug_addr.s | 2 +- 7 files changed, 16 insertions(+), 12 deletions(-) diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h index 98d7a7ee3cae10..36fdd511d1e259 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h @@ -427,14 +427,18 @@ class DWARFUnit { /// an entry in the rangelist table's offset array and is supplied by /// DW_FORM_rnglistx. Optional getRnglistOffset(uint32_t Index) { - if (RngListTable) - return RngListTable->getOffsetEntry(Index); + if (!RngListTable) + return None; + if (Optional Off = RngListTable->getOffsetEntry(Index)) + return *Off + RangeSectionBase; return None; } Optional getLoclistOffset(uint32_t Index) { - if (LoclistTableHeader) - return LoclistTableHeader->getOffsetEntry(Index); + if (!LoclistTableHeader) + return None; + if (Optional Off = LoclistTableHeader->getOffsetEntry(Index)) + return *Off + getLocSectionBase(); return None; } Expected collectAddressRanges(); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp index 404bc13b178a34..4b86359c04e3f3 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -91,7 +91,7 @@ static void dumpLocation(raw_ostream &OS, DWARFFormValue &FormValue, FormValue.dump(OS, DumpOpts); if (auto LoclistOffset = U->getLoclistOffset(Offset)) - Offset = *LoclistOffset + U->getLocSectionBase(); + Offset = *LoclistOffset; else return; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp index b662e88816f8a6..4ccda628093c9f 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -629,7 +629,7 @@ DWARFUnit::findRnglistFromOffset(uint64_t Offset) { Expected DWARFUnit::findRnglistFromIndex(uint32_t Index) { if (auto Offset = getRnglistOffset(Index)) - return findRnglistFromOffset(*Offset + RangeSectionBase); + return findRnglistFromOffset(*Offset); if (RngListTable) return createStringError(errc::invalid_argument, diff --git a/llvm/test/DebugInfo/X86/dwarfdump-rnglists-dwarf64.s b/llvm/test/DebugInfo/X86/dwarfdump-rnglists-dwarf64.s index f8395818734ed5..19bbd77586d8ff 100644 --- a/llvm/test/DebugInfo/X86/dwarfdump-rnglists-dwarf64.s +++ b/llvm/test/DebugInfo/X86/dwarfdump-rnglists-dwarf64.s @@ -196,14 +196,14 @@ Range1_end: # CHECK-NOT: Compile Unit: # CHECK: DW_TAG_compile_unit # CHECK-NEXT: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x00000014) -# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000020 +# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000034 # CHECK-NEXT: [0x0000002a, 0x00000034) ".text") # CHECK: .debug_info.dwo contents: # CHECK: Compile Unit: # CHECK-NOT: contents: # CHECK: DW_TAG_compile_unit -# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000011 +# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000025 # CHECK-NEXT: [0x0000002a, 0x00000034)) #ERR: error: parsing a range list table: did not detect a valid list table with base = 0x8 diff --git a/llvm/test/DebugInfo/X86/dwarfdump-rnglists.s b/llvm/test/DebugInfo/X86/dwarfdump-rnglists.s index 7886374c4d6374..0d6898df170bf8 100644 --- a/llvm/test/DebugInfo/X86/dwarfdump-rnglists.s +++ b/llvm/test/DebugInfo/X86/dwarfdump-rnglists.s @@ -192,14 +192,14 @@ Range1_end: # CHECK-NOT: Compile Unit: # CHECK: DW_TAG_compile_unit # CHECK-NEXT: DW_AT_rnglists_base [DW_FORM_sec_offset] (0x0000000c) -# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000018 +# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000024 # CHECK-NEXT: [0x0000002a, 0x00000034) ".text") # CHECK: .debug_info.dwo contents: # CHECK: Compile Unit: # CHECK-NOT: contents: # CHECK: DW_TAG_compile_unit -# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000009 +# CHECK-NEXT: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x1) rangelist = 0x00000015 # CHECK-NEXT: [0x0000002a, 0x00000034)) #ERR: error: parsing a range list table: did not detect a valid list table with base = 0x8 diff --git a/llvm/test/DebugInfo/X86/split-dwarf-v5-ranges.ll b/llvm/test/DebugInfo/X86/split-dwarf-v5-ranges.ll index 74e94643b9c082..1761c4aa8fe4b1 100644 --- a/llvm/test/DebugInfo/X86/split-dwarf-v5-ranges.ll +++ b/llvm/test/DebugInfo/X86/split-dwarf-v5-ranges.ll @@ -3,7 +3,7 @@ ; CHECK: .debug_info contents: ; CHECK: .debug_info.dwo contents: -; CHECK: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = 0x00000004 +; CHECK: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = 0x00000010 ; CHECK: [0x0000000000000001, 0x000000000000000c) ".text" ; CHECK: [0x000000000000000e, 0x0000000000000013) ".text") diff --git a/llvm/test/tools/llvm-dwarfdump/X86/no_debug_addr.s b/llvm/test/tools/llvm-dwarfdump/X86/no_debug_addr.s index ce1ae23cf8dc31..bf660679837be7 100644 --- a/llvm/test/tools/llvm-dwarfdump/X86/no_debug_addr.s +++ b/llvm/test/tools/llvm-dwarfdump/X86/no_debug_addr.s @@ -4,7 +4,7 @@ ## Ensure bogus empty section names are not printed when dumping ## rnglists that reference debug_addr when it is not present (such as in .dwo files) -# CHECK: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = 0x00000004 +# CHECK: DW_AT_ranges [DW_FORM_rnglistx] (indexed (0x0) rangelist = 0x00000010 # CHECK-NEXT: [0x0000000000000000, 0x0000000000000001) # CHECK-NEXT: [0x0000000000000000, 0x0000000000000002)) From c16f0b18c13e88fedaa510bc2442bb693a6230c8 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Tue, 26 Nov 2019 16:36:09 +0100 Subject: [PATCH 5/9] [lldb/cpluspluslanguage] Add constructor substitutor Summary: This patch adds code which will substitute references to the full object constructors/destructors with their base object versions. Like all substitutions in this category, this operation is not really sound, but doing this in a more precise way allows us to get rid of a much larger hack -- matching function according to their demangled names, which effectively does the same thing, but also much more. This is a (very late) follow-up to D54074. Background: clang has an optimization which can eliminate full object structors completely, if they are found to be equivalent to their base object versions. It does this because it assumes they can be regenerated on demand in the compile unit that needs them (e.g., because they are declared inline). However, this doesn't work for the debugging scenario, where we don't have the structor bodies available -- we pretend all constructors are defined out-of-line as far as clang is concerned. This causes clang to emit references to the (nonexisting) full object structors during expression evaluation. Fun fact: This is not a problem on darwin, because the relevant optimization is disabled to work around a linker bug. Reviewers: teemperor, JDevlieghere Subscribers: lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D70721 --- .../Language/CPlusPlus/CPlusPlusLanguage.cpp | 123 ++++++++++++------ .../CPlusPlus/CPlusPlusLanguageTest.cpp | 2 + 2 files changed, 84 insertions(+), 41 deletions(-) diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index c22f4ae9e41a92..4385a60f586239 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -284,46 +284,34 @@ class NodeAllocator { } }; -/// Given a mangled function `Mangled`, replace all the primitive function type -/// arguments of `Search` with type `Replace`. -class TypeSubstitutor - : public llvm::itanium_demangle::AbstractManglingParser +class ManglingSubstitutor + : public llvm::itanium_demangle::AbstractManglingParser { - /// Input character until which we have constructed the respective output - /// already - const char *Written; + using Base = + llvm::itanium_demangle::AbstractManglingParser; - llvm::StringRef Search; - llvm::StringRef Replace; - llvm::SmallString<128> Result; +public: + ManglingSubstitutor() : Base(nullptr, nullptr) {} - /// Whether we have performed any substitutions. - bool Substituted; + template + ConstString substitute(llvm::StringRef Mangled, Ts &&... Vals) { + this->getDerived().reset(Mangled, std::forward(Vals)...); + return substituteImpl(Mangled); + } - void reset(llvm::StringRef Mangled, llvm::StringRef Search, - llvm::StringRef Replace) { - AbstractManglingParser::reset(Mangled.begin(), Mangled.end()); + +protected: + void reset(llvm::StringRef Mangled) { + Base::reset(Mangled.begin(), Mangled.end()); Written = Mangled.begin(); - this->Search = Search; - this->Replace = Replace; Result.clear(); Substituted = false; } - void appendUnchangedInput() { - Result += llvm::StringRef(Written, First - Written); - Written = First; - } - -public: - TypeSubstitutor() : AbstractManglingParser(nullptr, nullptr) {} - - ConstString substitute(llvm::StringRef Mangled, llvm::StringRef From, - llvm::StringRef To) { + ConstString substituteImpl(llvm::StringRef Mangled) { Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_LANGUAGE); - - reset(Mangled, From, To); - if (parse() == nullptr) { + if (this->parse() == nullptr) { LLDB_LOG(log, "Failed to substitute mangling in {0}", Mangled); return ConstString(); } @@ -336,20 +324,69 @@ class TypeSubstitutor return ConstString(Result); } + void trySubstitute(llvm::StringRef From, llvm::StringRef To) { + if (!llvm::StringRef(currentParserPos(), this->numLeft()).startswith(From)) + return; + + // We found a match. Append unmodified input up to this point. + appendUnchangedInput(); + + // And then perform the replacement. + Result += To; + Written += From.size(); + Substituted = true; + } + +private: + /// Input character until which we have constructed the respective output + /// already. + const char *Written; + + llvm::SmallString<128> Result; + + /// Whether we have performed any substitutions. + bool Substituted; + + const char *currentParserPos() const { return this->First; } + + void appendUnchangedInput() { + Result += + llvm::StringRef(Written, std::distance(Written, currentParserPos())); + Written = currentParserPos(); + } + +}; + +/// Given a mangled function `Mangled`, replace all the primitive function type +/// arguments of `Search` with type `Replace`. +class TypeSubstitutor : public ManglingSubstitutor { + llvm::StringRef Search; + llvm::StringRef Replace; + +public: + void reset(llvm::StringRef Mangled, llvm::StringRef Search, + llvm::StringRef Replace) { + ManglingSubstitutor::reset(Mangled); + this->Search = Search; + this->Replace = Replace; + } + llvm::itanium_demangle::Node *parseType() { - if (llvm::StringRef(First, numLeft()).startswith(Search)) { - // We found a match. Append unmodified input up to this point. - appendUnchangedInput(); - - // And then perform the replacement. - Result += Replace; - Written += Search.size(); - Substituted = true; - } - return AbstractManglingParser::parseType(); + trySubstitute(Search, Replace); + return ManglingSubstitutor::parseType(); } }; -} + +class CtorDtorSubstitutor : public ManglingSubstitutor { +public: + llvm::itanium_demangle::Node * + parseCtorDtorName(llvm::itanium_demangle::Node *&SoFar, NameState *State) { + trySubstitute("C1", "C2"); + trySubstitute("D1", "D2"); + return ManglingSubstitutor::parseCtorDtorName(SoFar, State); + } +}; +} // namespace uint32_t CPlusPlusLanguage::FindAlternateFunctionManglings( const ConstString mangled_name, std::set &alternates) { @@ -397,6 +434,10 @@ uint32_t CPlusPlusLanguage::FindAlternateFunctionManglings( TS.substitute(mangled_name.GetStringRef(), "y", "m")) alternates.insert(ulong_fixup); + if (ConstString ctor_fixup = + CtorDtorSubstitutor().substitute(mangled_name.GetStringRef())) + alternates.insert(ctor_fixup); + return alternates.size() - start_size; } diff --git a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp index 150bef1590f4a5..deb6c7d54ea9f3 100644 --- a/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp +++ b/lldb/unittests/Language/CPlusPlus/CPlusPlusLanguageTest.cpp @@ -191,6 +191,8 @@ TEST(CPlusPlusLanguage, FindAlternateFunctionManglings) { EXPECT_THAT(FindAlternate("_ZN1A1fEx"), Contains("_ZN1A1fEl")); EXPECT_THAT(FindAlternate("_ZN1A1fEy"), Contains("_ZN1A1fEm")); EXPECT_THAT(FindAlternate("_ZN1A1fEai"), Contains("_ZN1A1fEci")); + EXPECT_THAT(FindAlternate("_ZN1AC1Ev"), Contains("_ZN1AC2Ev")); + EXPECT_THAT(FindAlternate("_ZN1AD1Ev"), Contains("_ZN1AD2Ev")); EXPECT_THAT(FindAlternate("_bogus"), IsEmpty()); } From 7f9b5138470db1dc58f3bc05631284c653c9ed7a Mon Sep 17 00:00:00 2001 From: Melanie Blower Date: Wed, 4 Dec 2019 12:23:46 -0800 Subject: [PATCH 6/9] Reapply af57dbf12e54 "Add support for options -frounding-math, ftrapping-math, -ffp-model=, and -ffp-exception-behavior=" Patch was reverted because https://bugs.llvm.org/show_bug.cgi?id=44048 The original patch is modified to set the strictfp IR attribute explicitly in CodeGen instead of as a side effect of IRBuilder. In the 2nd attempt to reapply there was a windows lit test fail, the tests were fixed to use wildcard matching. Differential Revision: https://reviews.llvm.org/D62731 --- clang/docs/UsersManual.rst | 54 ++++- clang/include/clang/AST/Decl.h | 4 + clang/include/clang/AST/DeclBase.h | 7 +- .../clang/Basic/DiagnosticDriverKinds.td | 4 + clang/include/clang/Basic/DiagnosticGroups.td | 3 + clang/include/clang/Basic/LangOptions.def | 2 + clang/include/clang/Basic/LangOptions.h | 28 +++ clang/include/clang/Driver/Options.td | 7 +- clang/lib/AST/Decl.cpp | 1 + clang/lib/CodeGen/CGCall.cpp | 14 ++ clang/lib/CodeGen/CodeGenFunction.cpp | 52 +++++ clang/lib/CodeGen/CodeGenFunction.h | 3 + clang/lib/Driver/ToolChains/Clang.cpp | 205 +++++++++++++++++- clang/lib/Frontend/CompilerInvocation.cpp | 28 +++ clang/lib/Sema/SemaExpr.cpp | 10 + clang/lib/Serialization/ASTReaderDecl.cpp | 1 + clang/lib/Serialization/ASTWriterDecl.cpp | 1 + clang/test/CodeGen/fpconstrained.c | 23 ++ clang/test/CodeGen/fpconstrained.cpp | 47 ++++ clang/test/Driver/clang_f_opts.c | 18 +- clang/test/Driver/fast-math.c | 4 +- clang/test/Driver/fp-model.c | 137 ++++++++++++ llvm/include/llvm/IR/IRBuilder.h | 1 - llvm/include/llvm/Target/TargetOptions.h | 2 +- llvm/unittests/IR/IRBuilderTest.cpp | 5 +- 25 files changed, 638 insertions(+), 23 deletions(-) create mode 100644 clang/test/CodeGen/fpconstrained.c create mode 100644 clang/test/CodeGen/fpconstrained.cpp create mode 100644 clang/test/Driver/fp-model.c diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 714681d7f4cea1..62e2575c6b26e5 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -1231,10 +1231,10 @@ are listed below. **-f[no-]trapping-math** - ``-fno-trapping-math`` allows optimizations that assume that - floating point operations cannot generate traps such as divide-by-zero, - overflow and underflow. Defaults to ``-ftrapping-math``. - Currently this option has no effect. + Control floating point exception behavior. ``-fno-trapping-math`` allows optimizations that assume that floating point operations cannot generate traps such as divide-by-zero, overflow and underflow. + +- The option ``-ftrapping-math`` behaves identically to ``-ffp-exception-behavior=strict``. +- The option ``-fno-trapping-math`` behaves identically to ``-ffp-exception-behavior=ignore``. This is the default. .. option:: -ffp-contract= @@ -1319,6 +1319,52 @@ are listed below. Defaults to ``-fno-finite-math``. +.. _opt_frounding-math: + +**-f[no-]rounding-math** + +Force floating-point operations to honor the dynamically-set rounding mode by default. + +The result of a floating-point operation often cannot be exactly represented in the result type and therefore must be rounded. IEEE 754 describes different rounding modes that control how to perform this rounding, not all of which are supported by all implementations. C provides interfaces (``fesetround`` and ``fesetenv``) for dynamically controlling the rounding mode, and while it also recommends certain conventions for changing the rounding mode, these conventions are not typically enforced in the ABI. Since the rounding mode changes the numerical result of operations, the compiler must understand something about it in order to optimize floating point operations. + +Note that floating-point operations performed as part of constant initialization are formally performed prior to the start of the program and are therefore not subject to the current rounding mode. This includes the initialization of global variables and local ``static`` variables. Floating-point operations in these contexts will be rounded using ``FE_TONEAREST``. + +- The option ``-fno-rounding-math`` allows the compiler to assume that the rounding mode is set to ``FE_TONEAREST``. This is the default. +- The option ``-frounding-math`` forces the compiler to honor the dynamically-set rounding mode. This prevents optimizations which might affect results if the rounding mode changes or is different from the default; for example, it prevents floating-point operations from being reordered across most calls and prevents constant-folding when the result is not exactly representable. + +.. option:: -ffp-model= + + Specify floating point behavior. ``-ffp-model`` is an umbrella + option that encompasses functionality provided by other, single + purpose, floating point options. Valid values are: ``precise``, ``strict``, + and ``fast``. + Details: + + * ``precise`` Disables optimizations that are not value-safe on floating-point data, although FP contraction (FMA) is enabled (``-ffp-contract=fast``). This is the default behavior. + * ``strict`` Enables ``-frounding-math`` and ``-ffp-exception-behavior=strict``, and disables contractions (FMA). All of the ``-ffast-math`` enablements are disabled. + * ``fast`` Behaves identically to specifying both ``-ffast-math`` and ``ffp-contract=fast`` + + Note: If your command line specifies multiple instances + of the ``-ffp-model`` option, or if your command line option specifies + ``-ffp-model`` and later on the command line selects a floating point + option that has the effect of negating part of the ``ffp-model`` that + has been selected, then the compiler will issue a diagnostic warning + that the override has occurred. + +.. option:: -ffp-exception-behavior= + + Specify the floating-point exception behavior. + + Valid values are: ``ignore``, ``maytrap``, and ``strict``. + The default value is ``ignore``. Details: + + * ``ignore`` The compiler assumes that the exception status flags will not be read and that floating point exceptions will be masked. + * ``maytrap`` The compiler avoids transformations that may raise exceptions that would not have been raised by the original code. Constant folding performed by the compiler is exempt from this option. + * ``strict`` The compiler ensures that all transformations strictly preserve the floating point exception semantics of the original code. + + + + .. _controlling-code-generation: Controlling Code Generation diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index c1544c9ded9417..f4913540bab4d7 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -2196,6 +2196,10 @@ class FunctionDecl : public DeclaratorDecl, bool usesSEHTry() const { return FunctionDeclBits.UsesSEHTry; } void setUsesSEHTry(bool UST) { FunctionDeclBits.UsesSEHTry = UST; } + /// Indicates the function uses Floating Point constrained intrinsics + bool usesFPIntrin() const { return FunctionDeclBits.UsesFPIntrin; } + void setUsesFPIntrin(bool Val) { FunctionDeclBits.UsesFPIntrin = Val; } + /// Whether this function has been deleted. /// /// A function that is "deleted" (via the C++0x "= delete" syntax) diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index adea10b33188b0..54cdb84b6f330b 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1534,10 +1534,13 @@ class DeclContext { /// Store the ODRHash after first calculation. uint64_t HasODRHash : 1; + + /// Indicates if the function uses Floating Point Constrained Intrinsics + uint64_t UsesFPIntrin : 1; }; /// Number of non-inherited bits in FunctionDeclBitfields. - enum { NumFunctionDeclBits = 25 }; + enum { NumFunctionDeclBits = 26 }; /// Stores the bits used by CXXConstructorDecl. If modified /// NumCXXConstructorDeclBits and the accessor @@ -1554,7 +1557,7 @@ class DeclContext { /// exactly 64 bits and thus the width of NumCtorInitializers /// will need to be shrunk if some bit is added to NumDeclContextBitfields, /// NumFunctionDeclBitfields or CXXConstructorDeclBitfields. - uint64_t NumCtorInitializers : 23; + uint64_t NumCtorInitializers : 22; uint64_t IsInheritingConstructor : 1; /// Whether this constructor has a trail-allocated explicit specifier. diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 39242c972ea28d..67faa872e57ccd 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -441,6 +441,10 @@ def warn_drv_experimental_isel_incomplete_opt : Warning< "-fexperimental-isel support is incomplete for this architecture at the current optimization level">, InGroup; +def warn_drv_experimental_fp_control_incomplete_opt : Warning< + "Support for floating point control option %0 is incomplete and experimental">, + InGroup; + def warn_drv_moutline_unsupported_opt : Warning< "The '%0' architecture does not support -moutline; flag ignored">, InGroup; diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 478b217a19f668..dec293f45af750 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1109,6 +1109,9 @@ def SpirCompat : DiagGroup<"spir-compat">; // Warning for the experimental-isel options. def ExperimentalISel : DiagGroup<"experimental-isel">; +// Warning for the experimental float control options. +def ExperimentalFloatControl : DiagGroup<"experimental-float-control">; + // A warning group specifically for warnings related to function // multiversioning. def FunctionMultiVersioning : DiagGroup<"function-multiversion">; diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 68d6ee1dce4231..05d96b6c6a1376 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -255,6 +255,8 @@ LANGOPT(SinglePrecisionConstants , 1, 0, "treating double-precision floating poi LANGOPT(FastRelaxedMath , 1, 0, "OpenCL fast relaxed math") /// FP_CONTRACT mode (on/off/fast). ENUM_LANGOPT(DefaultFPContractMode, FPContractModeKind, 2, FPC_Off, "FP contraction type") +ENUM_LANGOPT(FPRoundingMode, FPRoundingModeKind, 3, FPR_ToNearest, "FP Rounding Mode type") +ENUM_LANGOPT(FPExceptionMode, FPExceptionModeKind, 2, FPE_Ignore, "FP Exception Behavior Mode type") LANGOPT(NoBitFieldTypeAlign , 1, 0, "bit-field type alignment") LANGOPT(HexagonQdsp6Compat , 1, 0, "hexagon-qdsp6 backward compatibility") LANGOPT(ObjCAutoRefCount , 1, 0, "Objective-C automated reference counting") diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 95628280a4a020..ae4a4b2b9e8774 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -190,6 +190,34 @@ class LangOptions : public LangOptionsBase { FEA_On }; + // Values of the following enumerations correspond to metadata arguments + // specified for constrained floating-point intrinsics: + // http://llvm.org/docs/LangRef.html#constrained-floating-point-intrinsics. + + /// Possible rounding modes. + enum FPRoundingModeKind { + /// Rounding to nearest, corresponds to "round.tonearest". + FPR_ToNearest, + /// Rounding toward -Inf, corresponds to "round.downward". + FPR_Downward, + /// Rounding toward +Inf, corresponds to "round.upward". + FPR_Upward, + /// Rounding toward zero, corresponds to "round.towardzero". + FPR_TowardZero, + /// Is determined by runtime environment, corresponds to "round.dynamic". + FPR_Dynamic + }; + + /// Possible floating point exception behavior. + enum FPExceptionModeKind { + /// Assume that floating-point exceptions are masked. + FPE_Ignore, + /// Transformations do not cause new exceptions but may hide some. + FPE_MayTrap, + /// Strictly preserve the floating-point exception semantics. + FPE_Strict + }; + enum class LaxVectorConversionKind { /// Permit no implicit vector bitcasts. None, diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index a64d0acb06fe1d..8965131b900133 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -929,6 +929,10 @@ def : Flag<["-"], "fextended-identifiers">, Group; def : Flag<["-"], "fno-extended-identifiers">, Group, Flags<[Unsupported]>; def fhosted : Flag<["-"], "fhosted">, Group; def fdenormal_fp_math_EQ : Joined<["-"], "fdenormal-fp-math=">, Group, Flags<[CC1Option]>; +def ffp_model_EQ : Joined<["-"], "ffp-model=">, Group, Flags<[DriverOption]>, + HelpText<"Controls the semantics of floating-point calculations.">; +def ffp_exception_behavior_EQ : Joined<["-"], "ffp-exception-behavior=">, Group, Flags<[CC1Option]>, + HelpText<"Specifies the exception behavior of floating-point operations.">; def ffast_math : Flag<["-"], "ffast-math">, Group, Flags<[CC1Option]>, HelpText<"Allow aggressive, lossy floating-point optimizations">; def fno_fast_math : Flag<["-"], "fno-fast-math">, Group; @@ -1154,6 +1158,8 @@ def fno_honor_infinities : Flag<["-"], "fno-honor-infinities">, Group; // This option was originally misspelt "infinites" [sic]. def : Flag<["-"], "fhonor-infinites">, Alias; def : Flag<["-"], "fno-honor-infinites">, Alias; +def frounding_math : Flag<["-"], "frounding-math">, Group, Flags<[CC1Option]>; +def fno_rounding_math : Flag<["-"], "fno-rounding-math">, Group, Flags<[CC1Option]>; def ftrapping_math : Flag<["-"], "ftrapping-math">, Group, Flags<[CC1Option]>; def fno_trapping_math : Flag<["-"], "fno-trapping-math">, Group, Flags<[CC1Option]>; def ffp_contract : Joined<["-"], "ffp-contract=">, Group, @@ -3242,7 +3248,6 @@ defm profile_values : BooleanFFlag<"profile-values">, Group, Group; defm rename_registers : BooleanFFlag<"rename-registers">, Group; defm ripa : BooleanFFlag<"ripa">, Group; -defm rounding_math : BooleanFFlag<"rounding-math">, Group; defm schedule_insns : BooleanFFlag<"schedule-insns">, Group; defm schedule_insns2 : BooleanFFlag<"schedule-insns2">, Group; defm see : BooleanFFlag<"see">, Group; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index bfcf7926861fc1..0301110b7067f6 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2793,6 +2793,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC, FunctionDeclBits.ConstexprKind = ConstexprKind; FunctionDeclBits.InstantiationIsPending = false; FunctionDeclBits.UsesSEHTry = false; + FunctionDeclBits.UsesFPIntrin = false; FunctionDeclBits.HasSkippedBody = false; FunctionDeclBits.WillHaveBody = false; FunctionDeclBits.IsMultiVersion = false; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index ca6b1d409c2470..657c9260e6e6cd 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -4336,6 +4336,13 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, Callee.getAbstractInfo(), Attrs, CallingConv, /*AttrOnCallSite=*/true); + if (const FunctionDecl *FD = dyn_cast_or_null(CurFuncDecl)) + if (FD->usesFPIntrin()) + // All calls within a strictfp function are marked strictfp + Attrs = + Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex, + llvm::Attribute::StrictFP); + // Apply some call-site-specific attributes. // TODO: work this into building the attribute set. @@ -4385,6 +4392,13 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, SmallVector BundleList = getBundlesForFunclet(CalleePtr); + if (const FunctionDecl *FD = dyn_cast_or_null(CurFuncDecl)) + if (FD->usesFPIntrin()) + // All calls within a strictfp function are marked strictfp + Attrs = + Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex, + llvm::Attribute::StrictFP); + // Emit the actual call/invoke instruction. llvm::CallBase *CI; if (!InvokeDest) { diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 7f3be896a7b928..fd3020835a4d78 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -33,6 +33,8 @@ #include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Dominators.h" +#include "llvm/IR/FPEnv.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Operator.h" @@ -87,6 +89,7 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) FMF.setAllowReassoc(); } Builder.setFastMathFlags(FMF); + SetFPModel(); } CodeGenFunction::~CodeGenFunction() { @@ -102,6 +105,51 @@ CodeGenFunction::~CodeGenFunction() { CGM.getOpenMPRuntime().functionFinished(*this); } +// Map the LangOption for rounding mode into +// the corresponding enum in the IR. +static llvm::fp::RoundingMode ToConstrainedRoundingMD( + LangOptions::FPRoundingModeKind Kind) { + + switch (Kind) { + case LangOptions::FPR_ToNearest: return llvm::fp::rmToNearest; + case LangOptions::FPR_Downward: return llvm::fp::rmDownward; + case LangOptions::FPR_Upward: return llvm::fp::rmUpward; + case LangOptions::FPR_TowardZero: return llvm::fp::rmTowardZero; + case LangOptions::FPR_Dynamic: return llvm::fp::rmDynamic; + } + llvm_unreachable("Unsupported FP RoundingMode"); +} + +// Map the LangOption for exception behavior into +// the corresponding enum in the IR. +static llvm::fp::ExceptionBehavior ToConstrainedExceptMD( + LangOptions::FPExceptionModeKind Kind) { + + switch (Kind) { + case LangOptions::FPE_Ignore: return llvm::fp::ebIgnore; + case LangOptions::FPE_MayTrap: return llvm::fp::ebMayTrap; + case LangOptions::FPE_Strict: return llvm::fp::ebStrict; + } + llvm_unreachable("Unsupported FP Exception Behavior"); +} + +void CodeGenFunction::SetFPModel() { + auto fpRoundingMode = ToConstrainedRoundingMD( + getLangOpts().getFPRoundingMode()); + auto fpExceptionBehavior = ToConstrainedExceptMD( + getLangOpts().getFPExceptionMode()); + + if (fpExceptionBehavior == llvm::fp::ebIgnore && + fpRoundingMode == llvm::fp::rmToNearest) + // Constrained intrinsics are not used. + ; + else { + Builder.setIsFPConstrained(true); + Builder.setDefaultConstrainedRounding(fpRoundingMode); + Builder.setDefaultConstrainedExcept(fpExceptionBehavior); + } +} + CharUnits CodeGenFunction::getNaturalPointeeTypeAlignment(QualType T, LValueBaseInfo *BaseInfo, TBAAAccessInfo *TBAAInfo) { @@ -823,6 +871,10 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, if (FD->isMain()) Fn->addFnAttr(llvm::Attribute::NoRecurse); + if (const FunctionDecl *FD = dyn_cast_or_null(D)) + if (FD->usesFPIntrin()) + Fn->addFnAttr(llvm::Attribute::StrictFP); + // If a custom alignment is used, force realigning to this alignment on // any main function which certainly will need it. if (const FunctionDecl *FD = dyn_cast_or_null(D)) diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 7a2627ccf58b84..8f99b090b81815 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4169,6 +4169,9 @@ class CodeGenFunction : public CodeGenTypeCache { /// point operation, expressed as the maximum relative error in ulp. void SetFPAccuracy(llvm::Value *Val, float Accuracy); + /// SetFPModel - Control floating point behavior via fp-model settings. + void SetFPModel(); + private: llvm::MDNode *getRangeForLoadFromType(QualType Ty); void EmitReturnOfRValue(RValue RV, QualType Ty); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 03a6de812047b2..5f8c0cb8a2c1e2 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -2302,9 +2302,18 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, bool AssociativeMath = false; bool ReciprocalMath = false; bool SignedZeros = true; - bool TrappingMath = true; + bool TrappingMath = false; // Implemented via -ffp-exception-behavior + bool TrappingMathPresent = false; // Is trapping-math in args, and not + // overriden by ffp-exception-behavior? + bool RoundingFPMath = false; + bool RoundingMathPresent = false; // Is rounding-math in args? + // -ffp-model values: strict, fast, precise + StringRef FPModel = ""; + // -ffp-exception-behavior options: strict, maytrap, ignore + StringRef FPExceptionBehavior = ""; StringRef DenormalFPMath = ""; StringRef FPContract = ""; + bool StrictFPModel = false; if (const Arg *A = Args.getLastArg(options::OPT_flimited_precision_EQ)) { CmdArgs.push_back("-mlimit-float-precision"); @@ -2312,7 +2321,73 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, } for (const Arg *A : Args) { - switch (A->getOption().getID()) { + auto optID = A->getOption().getID(); + bool PreciseFPModel = false; + switch (optID) { + default: + break; + case options::OPT_frounding_math: + case options::OPT_ftrapping_math: + case options::OPT_ffp_exception_behavior_EQ: + D.Diag(clang::diag::warn_drv_experimental_fp_control_incomplete_opt) + << A->getOption().getName(); + break; + case options::OPT_ffp_model_EQ: { + D.Diag(clang::diag::warn_drv_experimental_fp_control_incomplete_opt) + << A->getOption().getName(); + // If -ffp-model= is seen, reset to fno-fast-math + HonorINFs = true; + HonorNaNs = true; + // Turning *off* -ffast-math restores the toolchain default. + MathErrno = TC.IsMathErrnoDefault(); + AssociativeMath = false; + ReciprocalMath = false; + SignedZeros = true; + // -fno_fast_math restores default denormal and fpcontract handling + DenormalFPMath = ""; + FPContract = ""; + StringRef Val = A->getValue(); + if (OFastEnabled && !Val.equals("fast")) { + // Only -ffp-model=fast is compatible with OFast, ignore. + D.Diag(clang::diag::warn_drv_overriding_flag_option) + << Args.MakeArgString("-ffp-model=" + Val) + << "-Ofast"; + break; + } + StrictFPModel = false; + PreciseFPModel = true; + // ffp-model= is a Driver option, it is entirely rewritten into more + // granular options before being passed into cc1. + // Use the gcc option in the switch below. + if (!FPModel.empty() && !FPModel.equals(Val)) { + D.Diag(clang::diag::warn_drv_overriding_flag_option) + << Args.MakeArgString("-ffp-model=" + FPModel) + << Args.MakeArgString("-ffp-model=" + Val); + FPContract = ""; + } + if (Val.equals("fast")) { + optID = options::OPT_ffast_math; + FPModel = Val; + FPContract = "fast"; + } else if (Val.equals("precise")) { + optID = options::OPT_ffp_contract; + FPModel = Val; + FPContract = "fast"; + PreciseFPModel = true; + } else if (Val.equals("strict")) { + StrictFPModel = true; + optID = options::OPT_frounding_math; + FPExceptionBehavior = "strict"; + FPModel = Val; + TrappingMath = true; + } else + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Val; + break; + } + } + + switch (optID) { // If this isn't an FP option skip the claim below default: continue; @@ -2329,19 +2404,82 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, case options::OPT_fno_reciprocal_math: ReciprocalMath = false; break; case options::OPT_fsigned_zeros: SignedZeros = true; break; case options::OPT_fno_signed_zeros: SignedZeros = false; break; - case options::OPT_ftrapping_math: TrappingMath = true; break; - case options::OPT_fno_trapping_math: TrappingMath = false; break; + case options::OPT_ftrapping_math: + if (!TrappingMathPresent && !FPExceptionBehavior.empty() && + !FPExceptionBehavior.equals("strict")) + // Warn that previous value of option is overridden. + D.Diag(clang::diag::warn_drv_overriding_flag_option) + << Args.MakeArgString("-ffp-exception-behavior=" + FPExceptionBehavior) + << "-ftrapping-math"; + TrappingMath = true; + TrappingMathPresent = true; + FPExceptionBehavior = "strict"; + break; + case options::OPT_fno_trapping_math: + if (!TrappingMathPresent && !FPExceptionBehavior.empty() && + !FPExceptionBehavior.equals("ignore")) + // Warn that previous value of option is overridden. + D.Diag(clang::diag::warn_drv_overriding_flag_option) + << Args.MakeArgString("-ffp-exception-behavior=" + FPExceptionBehavior) + << "-fno-trapping-math"; + TrappingMath = false; + TrappingMathPresent = true; + FPExceptionBehavior = "ignore"; + break; + + case options::OPT_frounding_math: + RoundingFPMath = true; + RoundingMathPresent = true; + break; + + case options::OPT_fno_rounding_math: + RoundingFPMath = false; + RoundingMathPresent = false; + break; case options::OPT_fdenormal_fp_math_EQ: DenormalFPMath = A->getValue(); break; - // Validate and pass through -fp-contract option. + // Validate and pass through -ffp-contract option. case options::OPT_ffp_contract: { StringRef Val = A->getValue(); - if (Val == "fast" || Val == "on" || Val == "off") + if (PreciseFPModel) { + // -ffp-model=precise enables ffp-contract=fast as a side effect + // the FPContract value has already been set to a string literal + // and the Val string isn't a pertinent value. + ; + } else if (Val.equals("fast") || Val.equals("on") || Val.equals("off")) FPContract = Val; else + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Val; + break; + } + + // Validate and pass through -ffp-model option. + case options::OPT_ffp_model_EQ: + // This should only occur in the error case + // since the optID has been replaced by a more granular + // floating point option. + break; + + // Validate and pass through -ffp-exception-behavior option. + case options::OPT_ffp_exception_behavior_EQ: { + StringRef Val = A->getValue(); + if (!TrappingMathPresent && !FPExceptionBehavior.empty() && + !FPExceptionBehavior.equals(Val)) + // Warn that previous value of option is overridden. + D.Diag(clang::diag::warn_drv_overriding_flag_option) + << Args.MakeArgString("-ffp-exception-behavior=" + FPExceptionBehavior) + << Args.MakeArgString("-ffp-exception-behavior=" + Val); + TrappingMath = TrappingMathPresent = false; + if (Val.equals("ignore") || Val.equals("maytrap")) + FPExceptionBehavior = Val; + else if (Val.equals("strict")) { + FPExceptionBehavior = Val; + TrappingMath = TrappingMathPresent = true; + } else D.Diag(diag::err_drv_unsupported_option_argument) << A->getOption().getName() << Val; break; @@ -2361,12 +2499,14 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, ReciprocalMath = true; SignedZeros = false; TrappingMath = false; + FPExceptionBehavior = ""; break; case options::OPT_fno_unsafe_math_optimizations: AssociativeMath = false; ReciprocalMath = false; SignedZeros = true; TrappingMath = true; + FPExceptionBehavior = "strict"; // -fno_unsafe_math_optimizations restores default denormal handling DenormalFPMath = ""; break; @@ -2384,6 +2524,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, ReciprocalMath = true; SignedZeros = false; TrappingMath = false; + RoundingFPMath = false; // If fast-math is set then set the fp-contract mode to fast. FPContract = "fast"; break; @@ -2397,12 +2538,31 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, AssociativeMath = false; ReciprocalMath = false; SignedZeros = true; - TrappingMath = true; + TrappingMath = false; + RoundingFPMath = false; // -fno_fast_math restores default denormal and fpcontract handling DenormalFPMath = ""; FPContract = ""; break; } + if (StrictFPModel) { + // If -ffp-model=strict has been specified on command line but + // subsequent options conflict then emit warning diagnostic. + if (HonorINFs && HonorNaNs && + !AssociativeMath && !ReciprocalMath && + SignedZeros && TrappingMath && RoundingFPMath && + DenormalFPMath.empty() && FPContract.empty()) + // OK: Current Arg doesn't conflict with -ffp-model=strict + ; + else { + StrictFPModel = false; + FPModel = ""; + D.Diag(clang::diag::warn_drv_overriding_flag_option) + << "-ffp-model=strict" << + ((A->getNumValues() == 0) ? A->getSpelling() + : Args.MakeArgString(A->getSpelling() + A->getValue())); + } + } // If we handled this option claim it A->claim(); @@ -2430,7 +2590,11 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, if (ReciprocalMath) CmdArgs.push_back("-freciprocal-math"); - if (!TrappingMath) + if (TrappingMath) { + // FP Exception Behavior is also set to strict + assert(FPExceptionBehavior.equals("strict")); + CmdArgs.push_back("-ftrapping-math"); + } else if (TrappingMathPresent) CmdArgs.push_back("-fno-trapping-math"); if (!DenormalFPMath.empty()) @@ -2440,14 +2604,37 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, if (!FPContract.empty()) CmdArgs.push_back(Args.MakeArgString("-ffp-contract=" + FPContract)); + if (!RoundingFPMath) + CmdArgs.push_back(Args.MakeArgString("-fno-rounding-math")); + + if (RoundingFPMath && RoundingMathPresent) + CmdArgs.push_back(Args.MakeArgString("-frounding-math")); + + if (!FPExceptionBehavior.empty()) + CmdArgs.push_back(Args.MakeArgString("-ffp-exception-behavior=" + + FPExceptionBehavior)); + ParseMRecip(D, Args, CmdArgs); // -ffast-math enables the __FAST_MATH__ preprocessor macro, but check for the // individual features enabled by -ffast-math instead of the option itself as // that's consistent with gcc's behaviour. if (!HonorINFs && !HonorNaNs && !MathErrno && AssociativeMath && - ReciprocalMath && !SignedZeros && !TrappingMath) + ReciprocalMath && !SignedZeros && !TrappingMath && !RoundingFPMath) { CmdArgs.push_back("-ffast-math"); + if (FPModel.equals("fast")) { + if (FPContract.equals("fast")) + // All set, do nothing. + ; + else if (FPContract.empty()) + // Enable -ffp-contract=fast + CmdArgs.push_back(Args.MakeArgString("-ffp-contract=fast")); + else + D.Diag(clang::diag::warn_drv_overriding_flag_option) + << "-ffp-model=fast" + << Args.MakeArgString("-ffp-contract=" + FPContract); + } + } // Handle __FINITE_MATH_ONLY__ similarly. if (!HonorINFs && !HonorNaNs) diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 74831e78d8cb9d..198ae69b76552b 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3145,6 +3145,34 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; } + LangOptions::FPRoundingModeKind FPRM = LangOptions::FPR_ToNearest; + if (Args.hasArg(OPT_frounding_math)) { + FPRM = LangOptions::FPR_Dynamic; + } + Opts.setFPRoundingMode(FPRM); + + if (Args.hasArg(OPT_ftrapping_math)) { + Opts.setFPExceptionMode(LangOptions::FPE_Strict); + } + + if (Args.hasArg(OPT_fno_trapping_math)) { + Opts.setFPExceptionMode(LangOptions::FPE_Ignore); + } + + LangOptions::FPExceptionModeKind FPEB = LangOptions::FPE_Ignore; + if (Arg *A = Args.getLastArg(OPT_ffp_exception_behavior_EQ)) { + StringRef Val = A->getValue(); + if (Val.equals("ignore")) + FPEB = LangOptions::FPE_Ignore; + else if (Val.equals("maytrap")) + FPEB = LangOptions::FPE_MayTrap; + else if (Val.equals("strict")) + FPEB = LangOptions::FPE_Strict; + else + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + } + Opts.setFPExceptionMode(FPEB); + Opts.RetainCommentsFromSystemHeaders = Args.hasArg(OPT_fretain_comments_from_system_headers); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 7bbda127a540aa..c53a4b789bedd5 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13037,6 +13037,16 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, if (ResultTy.isNull() || LHS.isInvalid() || RHS.isInvalid()) return ExprError(); + if (ResultTy->isRealFloatingType() && + (getLangOpts().getFPRoundingMode() != LangOptions::FPR_ToNearest || + getLangOpts().getFPExceptionMode() != LangOptions::FPE_Ignore)) + // Mark the current function as usng floating point constrained intrinsics + if (FunctionDecl *F = dyn_cast(CurContext)) +{ + F->setUsesFPIntrin(true); + printf("Enclosing function uses fp intrinsics\n"); +} + // Some of the binary operations require promoting operands of half vector to // float vectors and truncating the result back to half vector. For now, we do // this only when HalfArgsAndReturn is set (that is, when the target is arm or diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 3f7a1ed7fd5c2b..d989f46c4ab4b5 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -886,6 +886,7 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { FD->ODRHash = Record.readInt(); FD->setHasODRHash(true); + FD->setUsesFPIntrin(Record.readInt()); switch ((FunctionDecl::TemplatedKind)Record.readInt()) { case FunctionDecl::TK_NonTemplate: diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 51902a607ca123..38eb64e52e4ac5 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -559,6 +559,7 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { Record.AddSourceLocation(D->getEndLoc()); Record.push_back(D->getODRHash()); + Record.push_back(D->usesFPIntrin()); Record.push_back(D->getTemplatedKind()); switch (D->getTemplatedKind()) { diff --git a/clang/test/CodeGen/fpconstrained.c b/clang/test/CodeGen/fpconstrained.c new file mode 100644 index 00000000000000..0a890e2e702eb8 --- /dev/null +++ b/clang/test/CodeGen/fpconstrained.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -ftrapping-math -frounding-math -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=FPMODELSTRICT +// RUN: %clang_cc1 -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=PRECISE +// RUN: %clang_cc1 -ffast-math -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST +// RUN: %clang_cc1 -ffast-math -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST +// RUN: %clang_cc1 -ffast-math -ffp-contract=fast -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST +// RUN: %clang_cc1 -ffast-math -ffp-contract=fast -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=EXCEPT +// RUN: %clang_cc1 -ffast-math -ffp-contract=fast -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=MAYTRAP +float f0, f1, f2; + +void foo() { + // CHECK-LABEL: define {{.*}}void @foo() + + // MAYTRAP: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.maytrap") + // EXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // FPMODELSTRICT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // STRICTEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // STRICTNOEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + // PRECISE: fadd contract float %{{.*}}, %{{.*}} + // FAST: fadd fast + f0 = f1 + f2; + + // CHECK: ret +} diff --git a/clang/test/CodeGen/fpconstrained.cpp b/clang/test/CodeGen/fpconstrained.cpp new file mode 100644 index 00000000000000..7aa34c98a4879a --- /dev/null +++ b/clang/test/CodeGen/fpconstrained.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -x c++ -ftrapping-math -fexceptions -fcxx-exceptions -frounding-math -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=FPMODELSTRICT +// RUN: %clang_cc1 -x c++ -ffp-contract=fast -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s -check-prefix=PRECISE +// RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST +// RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST +// RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -ffp-exception-behavior=ignore -emit-llvm -o - %s | FileCheck %s -check-prefix=FAST +// RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -ffp-exception-behavior=strict -emit-llvm -o - %s | FileCheck %s -check-prefix=EXCEPT +// RUN: %clang_cc1 -x c++ -ffast-math -fexceptions -fcxx-exceptions -ffp-contract=fast -ffp-exception-behavior=maytrap -emit-llvm -o - %s | FileCheck %s -check-prefix=MAYTRAP +float f0, f1, f2; + + template + class aaaa { + public: + ~aaaa(); + void b(); + }; + + template + aaaa::~aaaa() { try { + b(); + // CHECK-LABEL: define {{.*}}void @_ZN4aaaaIiED2Ev{{.*}} + + } catch (...) { + // MAYTRAP: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.maytrap") + // EXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict") + // FPMODELSTRICT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // STRICTEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.strict") + // STRICTNOEXCEPT: llvm.experimental.constrained.fadd.f32(float %{{.*}}, float %{{.*}}, metadata !"round.dynamic", metadata !"fpexcept.ignore") + // PRECISE: fadd contract float %{{.*}}, %{{.*}} + // FAST: fadd fast + f0 = f1 + f2; + + // CHECK: ret void + } + } + + class d { + public: + d(const char *, int); + aaaa e; + }; + +float foo() { + d x("", 1); + aaaa a; + return f0; +} + diff --git a/clang/test/Driver/clang_f_opts.c b/clang/test/Driver/clang_f_opts.c index 17feaab26ab79f..fef9cbfb115e00 100644 --- a/clang/test/Driver/clang_f_opts.c +++ b/clang/test/Driver/clang_f_opts.c @@ -198,6 +198,22 @@ // CHECK-EXTENDED-IDENTIFIERS-NOT: "-fextended-identifiers" // CHECK-NO-EXTENDED-IDENTIFIERS: error: unsupported option '-fno-extended-identifiers' +// RUN: %clang -### -S -frounding-math %s 2>&1 | FileCheck -check-prefix=CHECK-ROUNDING-MATH %s +// CHECK-ROUNDING-MATH: "-cc1" +// CHECK-ROUNDING-MATH: "-frounding-math" +// CHECK-ROUNDING-MATH-NOT: "-fno-rounding-math" +// RUN: %clang -### -S %s 2>&1 | FileCheck -check-prefix=CHECK-ROUNDING-MATH-NOT %s +// RUN: %clang -### -S -ffp-model=imprecise %s 2>&1 | FileCheck -check-prefix=CHECK-FPMODEL %s +// CHECK-FPMODEL: unsupported argument 'imprecise' to option 'ffp-model=' +// RUN: %clang -### -S -ffp-model=precise %s 2>&1 | FileCheck -check-prefix=IGNORE %s +// RUN: %clang -### -S -ffp-model=strict %s 2>&1 | FileCheck -check-prefix=IGNORE %s +// RUN: %clang -### -S -ffp-model=fast %s 2>&1 | FileCheck -check-prefix=IGNORE %s +// RUN: %clang -### -S -ffp-exception-behavior=trap %s 2>&1 | FileCheck -check-prefix=CHECK-FPEB %s +// CHECK-FPEB: unsupported argument 'trap' to option 'ffp-exception-behavior=' +// RUN: %clang -### -S -ffp-exception-behavior=maytrap %s 2>&1 | FileCheck -check-prefix=IGNORE %s +// RUN: %clang -### -S -ffp-exception-behavior=ignore %s 2>&1 | FileCheck -check-prefix=IGNORE %s +// RUN: %clang -### -S -ffp-exception-behavior=strict %s 2>&1 | FileCheck -check-prefix=IGNORE %s + // RUN: %clang -### -S -fno-pascal-strings -mpascal-strings %s 2>&1 | FileCheck -check-prefix=CHECK-M-PASCAL-STRINGS %s // CHECK-M-PASCAL-STRINGS: "-fpascal-strings" @@ -320,7 +336,6 @@ // RUN: -fprefetch-loop-arrays \ // RUN: -fprofile-correction \ // RUN: -fprofile-values \ -// RUN: -frounding-math \ // RUN: -fschedule-insns \ // RUN: -fsignaling-nans \ // RUN: -fstrength-reduce \ @@ -385,7 +400,6 @@ // CHECK-WARNING-DAG: optimization flag '-fprefetch-loop-arrays' is not supported // CHECK-WARNING-DAG: optimization flag '-fprofile-correction' is not supported // CHECK-WARNING-DAG: optimization flag '-fprofile-values' is not supported -// CHECK-WARNING-DAG: optimization flag '-frounding-math' is not supported // CHECK-WARNING-DAG: optimization flag '-fschedule-insns' is not supported // CHECK-WARNING-DAG: optimization flag '-fsignaling-nans' is not supported // CHECK-WARNING-DAG: optimization flag '-fstrength-reduce' is not supported diff --git a/clang/test/Driver/fast-math.c b/clang/test/Driver/fast-math.c index 916384216d8c5f..da47de260dc902 100644 --- a/clang/test/Driver/fast-math.c +++ b/clang/test/Driver/fast-math.c @@ -170,11 +170,11 @@ // RUN: %clang -### -fno-fast-math -ffast-math -c %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-FAST-MATH %s // RUN: %clang -### -funsafe-math-optimizations -ffinite-math-only \ -// RUN: -fno-math-errno -ffp-contract=fast -c %s 2>&1 \ +// RUN: -fno-math-errno -ffp-contract=fast -fno-rounding-math -c %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-FAST-MATH %s // RUN: %clang -### -fno-honor-infinities -fno-honor-nans -fno-math-errno \ // RUN: -fassociative-math -freciprocal-math -fno-signed-zeros \ -// RUN: -fno-trapping-math -ffp-contract=fast -c %s 2>&1 \ +// RUN: -fno-trapping-math -ffp-contract=fast -fno-rounding-math -c %s 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-FAST-MATH %s // CHECK-FAST-MATH: "-cc1" // CHECK-FAST-MATH: "-ffast-math" diff --git a/clang/test/Driver/fp-model.c b/clang/test/Driver/fp-model.c new file mode 100644 index 00000000000000..a3984acef62b25 --- /dev/null +++ b/clang/test/Driver/fp-model.c @@ -0,0 +1,137 @@ +// Test that incompatible combinations of -ffp-model= options +// and other floating point options get a warning diagnostic. +// +// REQUIRES: clang-driver + +// RUN: %clang -### -ffp-model=fast -ffp-contract=off -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN %s +// WARN: warning: overriding '-ffp-model=fast' option with '-ffp-contract=off' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=fast -ffp-contract=on -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN1 %s +// WARN1: warning: overriding '-ffp-model=fast' option with '-ffp-contract=on' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -fassociative-math -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN2 %s +// WARN2: warning: overriding '-ffp-model=strict' option with '-fassociative-math' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -ffast-math -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN3 %s +// WARN3: warning: overriding '-ffp-model=strict' option with '-ffast-math' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -ffinite-math-only -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN4 %s +// WARN4: warning: overriding '-ffp-model=strict' option with '-ffinite-math-only' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -ffp-contract=fast -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN5 %s +// WARN5: warning: overriding '-ffp-model=strict' option with '-ffp-contract=fast' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -ffp-contract=off -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN6 %s +// WARN6: warning: overriding '-ffp-model=strict' option with '-ffp-contract=off' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -ffp-contract=on -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN7 %s +// WARN7: warning: overriding '-ffp-model=strict' option with '-ffp-contract=on' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -fno-honor-infinities -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN8 %s +// WARN8: warning: overriding '-ffp-model=strict' option with '-fno-honor-infinities' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -fno-honor-nans -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARN9 %s +// WARN9: warning: overriding '-ffp-model=strict' option with '-fno-honor-nans' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -fno-rounding-math -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARNa %s +// WARNa: warning: overriding '-ffp-model=strict' option with '-fno-rounding-math' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -fno-signed-zeros -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARNb %s +// WARNb: warning: overriding '-ffp-model=strict' option with '-fno-signed-zeros' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -fno-trapping-math -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARNc %s +// WARNc: warning: overriding '-ffp-model=strict' option with '-fno-trapping-math' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -freciprocal-math -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARNd %s +// WARNd: warning: overriding '-ffp-model=strict' option with '-freciprocal-math' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -funsafe-math-optimizations -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARNe %s +// WARNe: warning: overriding '-ffp-model=strict' option with '-funsafe-math-optimizations' [-Woverriding-t-option] + +// RUN: %clang -### -ffp-model=strict -Ofast -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=WARNf %s +// WARNf: warning: overriding '-ffp-model=strict' option with '-Ofast' [-Woverriding-t-option] + +// RUN: %clang -### -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOROUND %s +// CHECK-NOROUND: "-cc1" +// CHECK-NOROUND: "-fno-rounding-math" + +// RUN: %clang -### -frounding-math -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-ROUND --implicit-check-not ffp-exception-behavior=strict %s +// CHECK-ROUND: "-cc1" +// CHECK-ROUND: "-frounding-math" + +// RUN: %clang -### -ftrapping-math -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-TRAP %s +// CHECK-TRAP: "-cc1" +// CHECK-TRAP: "-ftrapping-math" +// CHECK-TRAP: "-ffp-exception-behavior=strict" + +// RUN: %clang -### -nostdinc -ffp-model=fast -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-FPM-FAST %s +// CHECK-FPM-FAST: "-cc1" +// CHECK-FPM-FAST: "-menable-no-infs" +// CHECK-FPM-FAST: "-menable-no-nans" +// CHECK-FPM-FAST: "-menable-unsafe-fp-math" +// CHECK-FPM-FAST: "-fno-signed-zeros" +// CHECK-FPM-FAST: "-mreassociate" +// CHECK-FPM-FAST: "-freciprocal-math" +// CHECK-FPM-FAST: "-ffp-contract=fast" +// CHECK-FPM-FAST: "-fno-rounding-math" +// CHECK-FPM-FAST: "-ffast-math" +// CHECK-FPM-FAST: "-ffinite-math-only" + +// RUN: %clang -### -nostdinc -ffp-model=precise -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-FPM-PRECISE %s +// CHECK-FPM-PRECISE: "-cc1" +// CHECK-FPM-PRECISE: "-ffp-contract=fast" +// CHECK-FPM-PRECISE: "-fno-rounding-math" + +// RUN: %clang -### -nostdinc -ffp-model=strict -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-FPM-STRICT %s +// CHECK-FPM-STRICT: "-cc1" +// CHECK-FPM-STRICT: "-ftrapping-math" +// CHECK-FPM-STRICT: "-frounding-math" +// CHECK-FPM-STRICT: "-ffp-exception-behavior=strict" + +// RUN: %clang -### -nostdinc -ftrapping-math -ffp-exception-behavior=ignore -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-TRAP-IGNORE %s +// CHECK-TRAP-IGNORE: "-cc1" +// CHECK-TRAP-IGNORE: "-fno-rounding-math" +// CHECK-TRAP-IGNORE: "-ffp-exception-behavior=ignore" + + +// RUN: %clang -### -nostdinc -ffp-exception-behavior=strict -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-FEB-STRICT %s +// CHECK-FEB-STRICT: "-cc1" +// CHECK-FEB-STRICT: "-fno-rounding-math" +// CHECK-FEB-STRICT: "-ffp-exception-behavior=strict" + +// RUN: %clang -### -nostdinc -ffp-exception-behavior=maytrap -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-FEB-MAYTRAP %s +// CHECK-FEB-MAYTRAP: "-cc1" +// CHECK-FEB-MAYTRAP: "-fno-rounding-math" +// CHECK-FEB-MAYTRAP: "-ffp-exception-behavior=maytrap" + +// RUN: %clang -### -nostdinc -ffp-exception-behavior=ignore -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-FEB-IGNORE %s +// CHECK-FEB-IGNORE: "-cc1" +// CHECK-FEB-IGNORE: "-fno-rounding-math" +// CHECK-FEB-IGNORE: "-ffp-exception-behavior=ignore" + diff --git a/llvm/include/llvm/IR/IRBuilder.h b/llvm/include/llvm/IR/IRBuilder.h index 2d9c72108d3d4f..24d39c2bc526fb 100644 --- a/llvm/include/llvm/IR/IRBuilder.h +++ b/llvm/include/llvm/IR/IRBuilder.h @@ -265,7 +265,6 @@ class IRBuilderBase { void setConstrainedFPCallAttr(CallInst *I) { if (!I->hasFnAttr(Attribute::StrictFP)) I->addAttribute(AttributeList::FunctionIndex, Attribute::StrictFP); - setConstrainedFPFunctionAttr(); } //===--------------------------------------------------------------------===// diff --git a/llvm/include/llvm/Target/TargetOptions.h b/llvm/include/llvm/Target/TargetOptions.h index c395e5bcecf179..d1db4eceabb883 100644 --- a/llvm/include/llvm/Target/TargetOptions.h +++ b/llvm/include/llvm/Target/TargetOptions.h @@ -107,7 +107,7 @@ namespace llvm { public: TargetOptions() : PrintMachineCode(false), UnsafeFPMath(false), NoInfsFPMath(false), - NoNaNsFPMath(false), NoTrappingFPMath(false), + NoNaNsFPMath(false), NoTrappingFPMath(true), NoSignedZerosFPMath(false), HonorSignDependentRoundingFPMathOption(false), NoZerosInBSS(false), GuaranteedTailCallOpt(false), StackSymbolOrdering(true), diff --git a/llvm/unittests/IR/IRBuilderTest.cpp b/llvm/unittests/IR/IRBuilderTest.cpp index d7712fda12707b..814da64c7852e9 100644 --- a/llvm/unittests/IR/IRBuilderTest.cpp +++ b/llvm/unittests/IR/IRBuilderTest.cpp @@ -183,6 +183,8 @@ TEST_F(IRBuilderTest, ConstrainedFP) { // See if we get constrained intrinsics instead of non-constrained // instructions. Builder.setIsFPConstrained(true); + auto Parent = BB->getParent(); + Parent->addFnAttr(Attribute::StrictFP); V = Builder.CreateFAdd(V, V); ASSERT_TRUE(isa(V)); @@ -233,7 +235,8 @@ TEST_F(IRBuilderTest, ConstrainedFP) { AttributeSet CallAttrs = II->getAttributes().getFnAttributes(); EXPECT_EQ(CallAttrs.hasAttribute(Attribute::StrictFP), true); - // Verify attributes on the containing function are created automatically. + // Verify attributes on the containing function are created when requested. + Builder.setConstrainedFPFunctionAttr(); AttributeList Attrs = BB->getParent()->getAttributes(); AttributeSet FnAttrs = Attrs.getFnAttributes(); EXPECT_EQ(FnAttrs.hasAttribute(Attribute::StrictFP), true); From f5767e284beaff4e5eb35f0f64270a070b47f6d3 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Wed, 4 Dec 2019 16:06:44 +0100 Subject: [PATCH 7/9] [lldb/DWARF] Switch to llvm debug_rnglists parser Summary: Our rnglist support was working only for the trivial cases (one CU), because we only ever parsed one contribution out of the debug_rnglists section. This means we were never able to resolve range lists for the second and subsequent units (DW_FORM_sec_offset references came out blang, and DW_FORM_rnglistx references always used the ranges lists from the first unit). Since both llvm and lldb rnglist parsers are sufficiently self-contained, and operate similarly, we can fix this problem by switching to the llvm parser instead. Besides the changes which are due to variations in the interface, the main thing is that now the range list object is a member of the DWARFUnit, instead of the entire symbol file. This ensures that each unit can get it's own private set of range list indices, and is consistent with how llvm's DWARFUnit does it (overall, I've tried to structure the code the same way as the llvm version). I've also added a test case for the two unit scenario. Reviewers: JDevlieghere, aprantl, clayborg Subscribers: dblaikie, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D71021 --- .../SymbolFile/DWARF/DWARFDebugInfoEntry.cpp | 9 +- .../SymbolFile/DWARF/DWARFDebugInfoEntry.h | 6 +- .../SymbolFile/DWARF/DWARFDebugRanges.cpp | 161 ------------------ .../SymbolFile/DWARF/DWARFDebugRanges.h | 23 --- .../Plugins/SymbolFile/DWARF/DWARFUnit.cpp | 98 +++++++++-- .../Plugins/SymbolFile/DWARF/DWARFUnit.h | 18 +- .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 15 -- .../SymbolFile/DWARF/SymbolFileDWARF.h | 3 - .../Shell/SymbolFile/DWARF/debug_rnglists.s | 55 +++++- 9 files changed, 157 insertions(+), 231 deletions(-) diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp index 8c0fbeb4b717b3..1bab4e9db63435 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp @@ -200,7 +200,7 @@ bool DWARFDebugInfoEntry::Extract(const DWARFDataExtractor &data, return false; } -static DWARFRangeList GetRangesOrReportError(const DWARFUnit &unit, +static DWARFRangeList GetRangesOrReportError(DWARFUnit &unit, const DWARFDebugInfoEntry &die, const DWARFFormValue &value) { llvm::Expected expected_ranges = @@ -223,7 +223,7 @@ static DWARFRangeList GetRangesOrReportError(const DWARFUnit &unit, // Gets the valid address ranges for a given DIE by looking for a // DW_AT_low_pc/DW_AT_high_pc pair, DW_AT_entry_pc, or DW_AT_ranges attributes. bool DWARFDebugInfoEntry::GetDIENamesAndRanges( - const DWARFUnit *cu, const char *&name, const char *&mangled, + DWARFUnit *cu, const char *&name, const char *&mangled, DWARFRangeList &ranges, int &decl_file, int &decl_line, int &decl_column, int &call_file, int &call_line, int &call_column, DWARFExpression *frame_base) const { @@ -766,7 +766,7 @@ bool DWARFDebugInfoEntry::GetAttributeAddressRange( } size_t DWARFDebugInfoEntry::GetAttributeAddressRanges( - const DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc, + DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc, bool check_specification_or_abstract_origin) const { ranges.Clear(); @@ -1012,8 +1012,7 @@ DWARFDebugInfoEntry::GetQualifiedName(DWARFUnit *cu, return storage.c_str(); } -bool DWARFDebugInfoEntry::LookupAddress(const dw_addr_t address, - const DWARFUnit *cu, +bool DWARFDebugInfoEntry::LookupAddress(const dw_addr_t address, DWARFUnit *cu, DWARFDebugInfoEntry **function_die, DWARFDebugInfoEntry **block_die) { bool found_address = false; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h index f3952ae9598b28..f35af6e7d498a9 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h @@ -50,7 +50,7 @@ class DWARFDebugInfoEntry { bool Extract(const lldb_private::DWARFDataExtractor &data, const DWARFUnit *cu, lldb::offset_t *offset_ptr); - bool LookupAddress(const dw_addr_t address, const DWARFUnit *cu, + bool LookupAddress(const dw_addr_t address, DWARFUnit *cu, DWARFDebugInfoEntry **function_die, DWARFDebugInfoEntry **block_die); @@ -91,7 +91,7 @@ class DWARFDebugInfoEntry { bool check_specification_or_abstract_origin = false) const; size_t GetAttributeAddressRanges( - const DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc, + DWARFUnit *cu, DWARFRangeList &ranges, bool check_hi_lo_pc, bool check_specification_or_abstract_origin = false) const; const char *GetName(const DWARFUnit *cu) const; @@ -116,7 +116,7 @@ class DWARFDebugInfoEntry { dw_attr_t attr, DWARFFormValue &form_value); bool GetDIENamesAndRanges( - const DWARFUnit *cu, const char *&name, const char *&mangled, + DWARFUnit *cu, const char *&name, const char *&mangled, DWARFRangeList &rangeList, int &decl_file, int &decl_line, int &decl_column, int &call_file, int &call_line, int &call_column, lldb_private::DWARFExpression *frame_base = nullptr) const; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp index 0b08fa09f90635..3b344f45091597 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp @@ -122,164 +122,3 @@ bool DWARFDebugRanges::FindRanges(const DWARFUnit *cu, } return false; } - -bool DWARFDebugRngLists::ExtractRangeList( - const DWARFDataExtractor &data, uint8_t addrSize, - lldb::offset_t *offset_ptr, std::vector &rangeList) { - rangeList.clear(); - - bool error = false; - while (!error) { - switch (data.GetU8(offset_ptr)) { - case DW_RLE_end_of_list: - return true; - - case DW_RLE_start_length: { - dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize); - dw_addr_t len = data.GetULEB128(offset_ptr); - rangeList.push_back({DW_RLE_start_length, begin, len}); - break; - } - - case DW_RLE_start_end: { - dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize); - dw_addr_t end = data.GetMaxU64(offset_ptr, addrSize); - rangeList.push_back({DW_RLE_start_end, begin, end}); - break; - } - - case DW_RLE_base_address: { - dw_addr_t base = data.GetMaxU64(offset_ptr, addrSize); - rangeList.push_back({DW_RLE_base_address, base, 0}); - break; - } - - case DW_RLE_offset_pair: { - dw_addr_t begin = data.GetULEB128(offset_ptr); - dw_addr_t end = data.GetULEB128(offset_ptr); - rangeList.push_back({DW_RLE_offset_pair, begin, end}); - break; - } - - case DW_RLE_base_addressx: { - dw_addr_t base = data.GetULEB128(offset_ptr); - rangeList.push_back({DW_RLE_base_addressx, base, 0}); - break; - } - - case DW_RLE_startx_endx: { - dw_addr_t start = data.GetULEB128(offset_ptr); - dw_addr_t end = data.GetULEB128(offset_ptr); - rangeList.push_back({DW_RLE_startx_endx, start, end}); - break; - } - - case DW_RLE_startx_length: { - dw_addr_t start = data.GetULEB128(offset_ptr); - dw_addr_t length = data.GetULEB128(offset_ptr); - rangeList.push_back({DW_RLE_startx_length, start, length}); - break; - } - - default: - lldbassert(0 && "unknown range list entry encoding"); - error = true; - } - } - - return false; -} - -static uint64_t ReadAddressFromDebugAddrSection(const DWARFUnit *cu, - uint32_t index) { - uint32_t index_size = cu->GetAddressByteSize(); - dw_offset_t addr_base = cu->GetAddrBase(); - lldb::offset_t offset = addr_base + index * index_size; - return cu->GetSymbolFileDWARF() - .GetDWARFContext() - .getOrLoadAddrData() - .GetMaxU64(&offset, index_size); -} - -bool DWARFDebugRngLists::FindRanges(const DWARFUnit *cu, - dw_offset_t debug_ranges_offset, - DWARFRangeList &range_list) const { - range_list.Clear(); - dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset; - auto pos = m_range_map.find(debug_ranges_address); - if (pos != m_range_map.end()) { - dw_addr_t BaseAddr = cu->GetBaseAddress(); - for (const RngListEntry &E : pos->second) { - switch (E.encoding) { - case DW_RLE_start_length: - range_list.Append(DWARFRangeList::Entry(E.value0, E.value1)); - break; - case DW_RLE_base_address: - BaseAddr = E.value0; - break; - case DW_RLE_start_end: - range_list.Append(DWARFRangeList::Entry(E.value0, E.value1 - E.value0)); - break; - case DW_RLE_offset_pair: - range_list.Append( - DWARFRangeList::Entry(BaseAddr + E.value0, E.value1 - E.value0)); - break; - case DW_RLE_base_addressx: { - BaseAddr = ReadAddressFromDebugAddrSection(cu, E.value0); - break; - } - case DW_RLE_startx_endx: { - dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0); - dw_addr_t end = ReadAddressFromDebugAddrSection(cu, E.value1); - range_list.Append(DWARFRangeList::Entry(start, end - start)); - break; - } - case DW_RLE_startx_length: { - dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0); - range_list.Append(DWARFRangeList::Entry(start, E.value1)); - break; - } - default: - llvm_unreachable("unexpected encoding"); - } - } - return true; - } - return false; -} - -void DWARFDebugRngLists::Extract(DWARFContext &context) { - const DWARFDataExtractor &data = context.getOrLoadRngListsData(); - lldb::offset_t offset = 0; - - uint64_t length = data.GetU32(&offset); - // FIXME: Handle DWARF64. - lldb::offset_t end = offset + length; - - // Check version. - if (data.GetU16(&offset) < 5) - return; - - uint8_t addrSize = data.GetU8(&offset); - - // We do not support non-zero segment selector size. - if (data.GetU8(&offset) != 0) { - lldbassert(0 && "not implemented"); - return; - } - - uint32_t offsetsAmount = data.GetU32(&offset); - for (uint32_t i = 0; i < offsetsAmount; ++i) - Offsets.push_back(data.GetMaxU64(&offset, 4)); - - lldb::offset_t listOffset = offset; - std::vector rangeList; - while (offset < end && ExtractRangeList(data, addrSize, &offset, rangeList)) { - m_range_map[listOffset] = rangeList; - listOffset = offset; - } -} - -uint64_t DWARFDebugRngLists::GetOffset(size_t Index) const { - return Offsets[Index]; -} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h index c398259056b3ea..99ef04d7ee214c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h @@ -48,27 +48,4 @@ class DWARFDebugRanges final : public DWARFDebugRangesBase { range_map m_range_map; }; -// DWARF v5 .debug_rnglists section. -class DWARFDebugRngLists final : public DWARFDebugRangesBase { - struct RngListEntry { - uint8_t encoding; - uint64_t value0; - uint64_t value1; - }; - -public: - void Extract(lldb_private::DWARFContext &context) override; - bool FindRanges(const DWARFUnit *cu, dw_offset_t debug_ranges_offset, - DWARFRangeList &range_list) const override; - uint64_t GetOffset(size_t Index) const; - -protected: - bool ExtractRangeList(const lldb_private::DWARFDataExtractor &data, - uint8_t addrSize, lldb::offset_t *offset_ptr, - std::vector &list); - - std::vector Offsets; - std::map> m_range_map; -}; - #endif // SymbolFileDWARF_DWARFDebugRanges_h_ diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp index 9964cf4b893c48..71375da844da74 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp @@ -417,8 +417,44 @@ dw_offset_t DWARFUnit::GetLineTableOffset() { void DWARFUnit::SetAddrBase(dw_addr_t addr_base) { m_addr_base = addr_base; } +// Parse the rangelist table header, including the optional array of offsets +// following it (DWARF v5 and later). +template +static llvm::Expected +ParseListTableHeader(const llvm::DWARFDataExtractor &data, uint64_t offset, + DwarfFormat format) { + // We are expected to be called with Offset 0 or pointing just past the table + // header. Correct Offset in the latter case so that it points to the start + // of the header. + if (offset > 0) { + uint64_t HeaderSize = llvm::DWARFListTableHeader::getHeaderSize(format); + if (offset < HeaderSize) + return llvm::createStringError(errc::invalid_argument, + "did not detect a valid" + " list table with base = 0x%" PRIx64 "\n", + offset); + offset -= HeaderSize; + } + ListTableType Table; + if (llvm::Error E = Table.extractHeaderAndOffsets(data, &offset)) + return std::move(E); + return Table; +} + void DWARFUnit::SetRangesBase(dw_addr_t ranges_base) { m_ranges_base = ranges_base; + + if (GetVersion() < 5) + return; + + if (auto table_or_error = ParseListTableHeader( + m_dwarf.GetDWARFContext().getOrLoadRngListsData().GetAsLLVM(), + ranges_base, DWARF32)) + m_rnglist_table = std::move(table_or_error.get()); + else + GetSymbolFileDWARF().GetObjectFile()->GetModule()->ReportError( + "Failed to extract range list table at offset 0x%" PRIx64 ": %s", + ranges_base, toString(table_or_error.takeError()).c_str()); } void DWARFUnit::SetStrOffsetsBase(dw_offset_t str_offsets_base) { @@ -845,30 +881,56 @@ uint32_t DWARFUnit::GetHeaderByteSize() const { } llvm::Expected -DWARFUnit::FindRnglistFromOffset(dw_offset_t offset) const { - const DWARFDebugRangesBase *debug_ranges; - llvm::StringRef section; +DWARFUnit::FindRnglistFromOffset(dw_offset_t offset) { if (GetVersion() <= 4) { - debug_ranges = m_dwarf.GetDebugRanges(); - section = "debug_ranges"; - } else { - debug_ranges = m_dwarf.GetDebugRngLists(); - section = "debug_rnglists"; + const DWARFDebugRangesBase *debug_ranges = m_dwarf.GetDebugRanges(); + if (!debug_ranges) + return llvm::make_error( + "No debug_ranges section"); + DWARFRangeList ranges; + debug_ranges->FindRanges(this, offset, ranges); + return ranges; } - if (!debug_ranges) - return llvm::make_error("No " + section + - " section"); + + if (!m_rnglist_table) + return llvm::createStringError(errc::invalid_argument, + "missing or invalid range list table"); + + auto range_list_or_error = m_rnglist_table->findList( + m_dwarf.GetDWARFContext().getOrLoadRngListsData().GetAsLLVM(), offset); + if (!range_list_or_error) + return range_list_or_error.takeError(); + + llvm::Expected llvm_ranges = + range_list_or_error->getAbsoluteRanges( + llvm::object::SectionedAddress{GetBaseAddress()}, + [&](uint32_t index) { + uint32_t index_size = GetAddressByteSize(); + dw_offset_t addr_base = GetAddrBase(); + lldb::offset_t offset = addr_base + index * index_size; + return llvm::object::SectionedAddress{ + m_dwarf.GetDWARFContext().getOrLoadAddrData().GetMaxU64( + &offset, index_size)}; + }); + if (!llvm_ranges) + return llvm_ranges.takeError(); DWARFRangeList ranges; - debug_ranges->FindRanges(this, offset, ranges); + for (const llvm::DWARFAddressRange &llvm_range : *llvm_ranges) { + ranges.Append(DWARFRangeList::Entry(llvm_range.LowPC, + llvm_range.HighPC - llvm_range.LowPC)); + } return ranges; } llvm::Expected -DWARFUnit::FindRnglistFromIndex(uint32_t index) const { - const DWARFDebugRngLists *debug_rnglists = m_dwarf.GetDebugRngLists(); - if (!debug_rnglists) - return llvm::make_error( - "No debug_rnglists section"); - return FindRnglistFromOffset(debug_rnglists->GetOffset(index)); +DWARFUnit::FindRnglistFromIndex(uint32_t index) { + if (llvm::Optional offset = GetRnglistOffset(index)) + return FindRnglistFromOffset(*offset); + if (m_rnglist_table) + return llvm::createStringError(errc::invalid_argument, + "invalid range list table index %d", index); + + return llvm::createStringError(errc::invalid_argument, + "missing or invalid range list table"); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h index 87e0de283de4b2..fe64222f8f50b2 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h @@ -216,12 +216,23 @@ class DWARFUnit : public lldb_private::UserID { /// Return a list of address ranges resulting from a (possibly encoded) /// range list starting at a given offset in the appropriate ranges section. - llvm::Expected FindRnglistFromOffset(dw_offset_t offset) const; + llvm::Expected FindRnglistFromOffset(dw_offset_t offset); /// Return a list of address ranges retrieved from an encoded range /// list whose offset is found via a table lookup given an index (DWARF v5 /// and later). - llvm::Expected FindRnglistFromIndex(uint32_t index) const; + llvm::Expected FindRnglistFromIndex(uint32_t index); + + /// Return a rangelist's offset based on an index. The index designates + /// an entry in the rangelist table's offset array and is supplied by + /// DW_FORM_rnglistx. + llvm::Optional GetRnglistOffset(uint32_t Index) const { + if (!m_rnglist_table) + return llvm::None; + if (llvm::Optional off = m_rnglist_table->getOffsetEntry(Index)) + return *off + m_ranges_base; + return llvm::None; + } protected: DWARFUnit(SymbolFileDWARF &dwarf, lldb::user_id_t uid, @@ -288,6 +299,9 @@ class DWARFUnit : public lldb_private::UserID { dw_offset_t m_line_table_offset = DW_INVALID_OFFSET; dw_offset_t m_str_offsets_base = 0; // Value of DW_AT_str_offsets_base. + + llvm::Optional m_rnglist_table; + const DIERef::Section m_section; private: diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index fc8fe30101cbca..9b9077a450b3ad 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -684,21 +684,6 @@ DWARFDebugRanges *SymbolFileDWARF::GetDebugRanges() { return m_ranges.get(); } -DWARFDebugRngLists *SymbolFileDWARF::GetDebugRngLists() { - if (!m_rnglists) { - static Timer::Category func_cat(LLVM_PRETTY_FUNCTION); - Timer scoped_timer(func_cat, "%s this = %p", LLVM_PRETTY_FUNCTION, - static_cast(this)); - - if (m_context.getOrLoadRngListsData().GetByteSize() > 0) - m_rnglists.reset(new DWARFDebugRngLists()); - - if (m_rnglists) - m_rnglists->Extract(m_context); - } - return m_rnglists.get(); -} - lldb::CompUnitSP SymbolFileDWARF::ParseCompileUnit(DWARFCompileUnit &dwarf_cu) { CompUnitSP cu_sp; CompileUnit *comp_unit = (CompileUnit *)dwarf_cu.GetUserData(); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 9e4e4279eec9fc..35b18f4b02b35b 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -47,7 +47,6 @@ class DWARFDebugInfo; class DWARFDebugInfoEntry; class DWARFDebugLine; class DWARFDebugRanges; -class DWARFDebugRngLists; class DWARFDeclContext; class DWARFFormValue; class DWARFTypeUnit; @@ -236,7 +235,6 @@ class SymbolFileDWARF : public lldb_private::SymbolFile, const DWARFDebugInfo *DebugInfo() const; DWARFDebugRanges *GetDebugRanges(); - DWARFDebugRngLists *GetDebugRngLists(); const lldb_private::DWARFDataExtractor &DebugLocData(); @@ -499,7 +497,6 @@ class SymbolFileDWARF : public lldb_private::SymbolFile, typedef llvm::StringMap NameToOffsetMap; NameToOffsetMap m_function_scope_qualified_name_map; std::unique_ptr m_ranges; - std::unique_ptr m_rnglists; UniqueDWARFASTTypeMap m_unique_ast_type_map; DIEToTypePtr m_die_to_type; DIEToVariableSP m_die_to_variable_sp; diff --git a/lldb/test/Shell/SymbolFile/DWARF/debug_rnglists.s b/lldb/test/Shell/SymbolFile/DWARF/debug_rnglists.s index 7daa32da8affec..1d718054a58777 100644 --- a/lldb/test/Shell/SymbolFile/DWARF/debug_rnglists.s +++ b/lldb/test/Shell/SymbolFile/DWARF/debug_rnglists.s @@ -1,12 +1,19 @@ # REQUIRES: x86 # RUN: llvm-mc -triple=x86_64-pc-linux -filetype=obj %s > %t -# RUN: %lldb %t -o "image lookup -v -s lookup_rnglists" -o exit | FileCheck %s +# RUN: %lldb %t -o "image lookup -v -s lookup_rnglists" \ +# RUN: -o "image lookup -v -s lookup_rnglists2" -o exit | FileCheck %s +# CHECK-LABEL: image lookup -v -s lookup_rnglists # CHECK: Function: id = {0x7fffffff00000030}, name = "rnglists", range = [0x0000000000000000-0x0000000000000004) # CHECK: Blocks: id = {0x7fffffff00000030}, range = [0x00000000-0x00000004) # CHECK-NEXT: id = {0x7fffffff00000046}, ranges = [0x00000001-0x00000002)[0x00000003-0x00000004) +# CHECK-LABEL: image lookup -v -s lookup_rnglists2 +# CHECK: Function: id = {0x7fffffff0000007a}, name = "rnglists2", range = [0x0000000000000004-0x0000000000000007) +# CHECK: Blocks: id = {0x7fffffff0000007a}, range = [0x00000004-0x00000007) +# CHECK-NEXT: id = {0x7fffffff00000091}, range = [0x00000005-0x00000007) + .text .p2align 12 rnglists: @@ -21,6 +28,15 @@ lookup_rnglists: .Lblock2_end: .Lrnglists_end: +rnglists2: + nop +.Lblock3_begin: +lookup_rnglists2: + nop + nop +.Lblock3_end: +.Lrnglists2_end: + .section .debug_abbrev,"",@progbits .byte 1 # Abbreviation Code .byte 17 # DW_TAG_compile_unit @@ -78,6 +94,28 @@ lookup_rnglists: .byte 0 # End Of Children Mark .Ldebug_info_end0: +.Lcu_begin1: + .long .Ldebug_info_end1-.Ldebug_info_start1 # Length of Unit +.Ldebug_info_start1: + .short 5 # DWARF version number + .byte 1 # DWARF Unit Type + .byte 8 # Address Size (in bytes) + .long .debug_abbrev # Offset Into Abbrev. Section + .byte 1 # Abbrev [1] 0xc:0x5f DW_TAG_compile_unit + .asciz "Hand-written DWARF" # DW_AT_producer + .quad rnglists2 # DW_AT_low_pc + .long .Lrnglists2_end-rnglists2 # DW_AT_high_pc + .long .Lrnglists_table_base1 # DW_AT_rnglists_base + .byte 2 # Abbrev [2] 0x2b:0x37 DW_TAG_subprogram + .quad rnglists2 # DW_AT_low_pc + .long .Lrnglists2_end-rnglists2 # DW_AT_high_pc + .asciz "rnglists2" # DW_AT_name + .byte 5 # Abbrev [5] 0x52:0xf DW_TAG_lexical_block + .byte 0 # DW_AT_ranges + .byte 0 # End Of Children Mark + .byte 0 # End Of Children Mark +.Ldebug_info_end1: + .section .debug_rnglists,"",@progbits .long .Ldebug_rnglist_table_end0-.Ldebug_rnglist_table_start0 # Length .Ldebug_rnglist_table_start0: @@ -96,3 +134,18 @@ lookup_rnglists: .uleb128 .Lblock2_end-rnglists # ending offset .byte 0 # DW_RLE_end_of_list .Ldebug_rnglist_table_end0: + + .long .Ldebug_rnglist_table_end1-.Ldebug_rnglist_table_start1 # Length +.Ldebug_rnglist_table_start1: + .short 5 # Version + .byte 8 # Address size + .byte 0 # Segment selector size + .long 1 # Offset entry count +.Lrnglists_table_base1: + .long .Ldebug_ranges1-.Lrnglists_table_base1 +.Ldebug_ranges1: + .byte 4 # DW_RLE_offset_pair + .uleb128 .Lblock3_begin-rnglists2 # starting offset + .uleb128 .Lblock3_end-rnglists2 # ending offset + .byte 0 # DW_RLE_end_of_list +.Ldebug_rnglist_table_end1: From 11a9bae8f66986751078501988b4414f24dbe37e Mon Sep 17 00:00:00 2001 From: Victor Lomuller Date: Tue, 25 Jun 2019 13:57:48 +0100 Subject: [PATCH 8/9] [AST] Enable expression of OpenCL language address spaces an attribute Summary: Enable a way to set OpenCL language address space using attributes in addition to existing keywords. Signed-off-by: Victor Lomuller victor@codeplay.com Reviewers: aaron.ballman, Anastasia Subscribers: yaxunl, ebevhan, cfe-commits, Naghasan Tags: #clang Differential Revision: https://reviews.llvm.org/D71005 Signed-off-by: Alexey Bader --- clang/include/clang/Basic/Attr.td | 10 +++--- clang/lib/Sema/SemaType.cpp | 16 +++++++-- .../AST/language_address_space_attribute.cpp | 36 +++++++++++++++++++ clang/test/SemaOpenCL/address-spaces.cl | 16 +++++++++ 4 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 clang/test/AST/language_address_space_attribute.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 4ea1c9f58bebc0..9ca4be0e07c892 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1120,27 +1120,27 @@ def OpenCLAccess : Attr { } def OpenCLPrivateAddressSpace : TypeAttr { - let Spellings = [Keyword<"__private">, Keyword<"private">]; + let Spellings = [Keyword<"__private">, Keyword<"private">, Clang<"opencl_private">]; let Documentation = [OpenCLAddressSpacePrivateDocs]; } def OpenCLGlobalAddressSpace : TypeAttr { - let Spellings = [Keyword<"__global">, Keyword<"global">]; + let Spellings = [Keyword<"__global">, Keyword<"global">, Clang<"opencl_global">]; let Documentation = [OpenCLAddressSpaceGlobalDocs]; } def OpenCLLocalAddressSpace : TypeAttr { - let Spellings = [Keyword<"__local">, Keyword<"local">]; + let Spellings = [Keyword<"__local">, Keyword<"local">, Clang<"opencl_local">]; let Documentation = [OpenCLAddressSpaceLocalDocs]; } def OpenCLConstantAddressSpace : TypeAttr { - let Spellings = [Keyword<"__constant">, Keyword<"constant">]; + let Spellings = [Keyword<"__constant">, Keyword<"constant">, Clang<"opencl_constant">]; let Documentation = [OpenCLAddressSpaceConstantDocs]; } def OpenCLGenericAddressSpace : TypeAttr { - let Spellings = [Keyword<"__generic">, Keyword<"generic">]; + let Spellings = [Keyword<"__generic">, Keyword<"generic">, Clang<"opencl_generic">]; let Documentation = [OpenCLAddressSpaceGenericDocs]; } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 52a0581643bca1..1375ccbabc5081 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -7407,6 +7407,16 @@ static void HandleLifetimeBoundAttr(TypeProcessingState &State, } } +static bool isAddressSpaceKind(const ParsedAttr &attr) { + auto attrKind = attr.getKind(); + + return attrKind == ParsedAttr::AT_AddressSpace || + attrKind == ParsedAttr::AT_OpenCLPrivateAddressSpace || + attrKind == ParsedAttr::AT_OpenCLGlobalAddressSpace || + attrKind == ParsedAttr::AT_OpenCLLocalAddressSpace || + attrKind == ParsedAttr::AT_OpenCLConstantAddressSpace || + attrKind == ParsedAttr::AT_OpenCLGenericAddressSpace; +} static void processTypeAttrs(TypeProcessingState &state, QualType &type, TypeAttrLocation TAL, @@ -7445,11 +7455,11 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, if (!IsTypeAttr) continue; } - } else if (TAL != TAL_DeclChunk && - attr.getKind() != ParsedAttr::AT_AddressSpace) { + } else if (TAL != TAL_DeclChunk && !isAddressSpaceKind(attr)) { // Otherwise, only consider type processing for a C++11 attribute if // it's actually been applied to a type. - // We also allow C++11 address_space attributes to pass through. + // We also allow C++11 address_space and + // OpenCL language address space attributes to pass through. continue; } } diff --git a/clang/test/AST/language_address_space_attribute.cpp b/clang/test/AST/language_address_space_attribute.cpp new file mode 100644 index 00000000000000..7c6bdca06c06a0 --- /dev/null +++ b/clang/test/AST/language_address_space_attribute.cpp @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 %s -ast-dump | FileCheck %s + +// Verify that the language address space attribute is +// understood correctly by clang. + +void langas() { + // CHECK: VarDecl {{.*}} x_global '__global int *' + __attribute__((opencl_global)) int *x_global; + + // CHECK: VarDecl {{.*}} z_global '__global int *' + [[clang::opencl_global]] int *z_global; + + // CHECK: VarDecl {{.*}} x_local '__local int *' + __attribute__((opencl_local)) int *x_local; + + // CHECK: VarDecl {{.*}} z_local '__local int *' + [[clang::opencl_local]] int *z_local; + + // CHECK: VarDecl {{.*}} x_constant '__constant int *' + __attribute__((opencl_constant)) int *x_constant; + + // CHECK: VarDecl {{.*}} z_constant '__constant int *' + [[clang::opencl_constant]] int *z_constant; + + // CHECK: VarDecl {{.*}} x_private 'int *' + __attribute__((opencl_private)) int *x_private; + + // CHECK: VarDecl {{.*}} z_private 'int *' + [[clang::opencl_private]] int *z_private; + + // CHECK: VarDecl {{.*}} x_generic '__generic int *' + __attribute__((opencl_generic)) int *x_generic; + + // CHECK: VarDecl {{.*}} z_generic '__generic int *' + [[clang::opencl_generic]] int *z_generic; +} diff --git a/clang/test/SemaOpenCL/address-spaces.cl b/clang/test/SemaOpenCL/address-spaces.cl index 09a6dd0ba53fe7..a28069470177c0 100644 --- a/clang/test/SemaOpenCL/address-spaces.cl +++ b/clang/test/SemaOpenCL/address-spaces.cl @@ -248,3 +248,19 @@ __kernel void k() { unsigned data[16]; func_with_array_param(data); } + +void func_multiple_addr2(void) { + typedef __private int private_int_t; + __private __attribute__((opencl_global)) int var1; // expected-error {{multiple address spaces specified for type}} + __private __attribute__((opencl_global)) int *var2; // expected-error {{multiple address spaces specified for type}} + __attribute__((opencl_global)) private_int_t var3; // expected-error {{multiple address spaces specified for type}} + __attribute__((opencl_global)) private_int_t *var4; // expected-error {{multiple address spaces specified for type}} + __attribute__((opencl_private)) private_int_t var5; // expected-warning {{multiple identical address spaces specified for type}} + __attribute__((opencl_private)) private_int_t *var6; // expected-warning {{multiple identical address spaces specified for type}} +#if __OPENCL_CPP_VERSION__ + [[clang::opencl_private]] __global int var7; // expected-error {{multiple address spaces specified for type}} + [[clang::opencl_private]] __global int *var8; // expected-error {{multiple address spaces specified for type}} + [[clang::opencl_private]] private_int_t var9; // expected-warning {{multiple identical address spaces specified for type}} + [[clang::opencl_private]] private_int_t *var10; // expected-warning {{multiple identical address spaces specified for type}} +#endif // !__OPENCL_CPP_VERSION__ +} From 09311459e3750f9dbd164fe7ea40fd9548571128 Mon Sep 17 00:00:00 2001 From: Roman Lebedev Date: Thu, 5 Dec 2019 15:10:25 +0300 Subject: [PATCH 9/9] [InstCombine] Extend `0 - (X sdiv C) -> (X sdiv -C)` fold to non-splat vectors Split off from https://reviews.llvm.org/D68408 --- llvm/include/llvm/IR/Constant.h | 7 +++++- llvm/lib/IR/Constants.cpp | 24 +++++++++++++++++++ .../InstCombine/InstCombineAddSub.cpp | 18 +++++++------- llvm/test/Transforms/InstCombine/div.ll | 9 +++---- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/llvm/include/llvm/IR/Constant.h b/llvm/include/llvm/IR/Constant.h index b91100a0e8913c..3f3fa4c272c5f7 100644 --- a/llvm/include/llvm/IR/Constant.h +++ b/llvm/include/llvm/IR/Constant.h @@ -53,6 +53,10 @@ class Constant : public User { /// Returns true if the value is one. bool isOneValue() const; + /// Return true if the value is not the one value, or, + /// for vectors, does not contain one value elements. + bool isNotOneValue() const; + /// Return true if this is the value that would be returned by /// getAllOnesValue. bool isAllOnesValue() const; @@ -64,7 +68,8 @@ class Constant : public User { /// Return true if the value is negative zero or null value. bool isZeroValue() const; - /// Return true if the value is not the smallest signed value. + /// Return true if the value is not the smallest signed value, or, + /// for vectors, does not contain smallest signed value elements. bool isNotMinSignedValue() const; /// Return true if the value is the smallest signed value. diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp index b5df4ea8af6c6f..7ea5cb8b167b02 100644 --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -150,6 +150,30 @@ bool Constant::isOneValue() const { return false; } +bool Constant::isNotOneValue() const { + // Check for 1 integers + if (const ConstantInt *CI = dyn_cast(this)) + return !CI->isOneValue(); + + // Check for FP which are bitcasted from 1 integers + if (const ConstantFP *CFP = dyn_cast(this)) + return !CFP->getValueAPF().bitcastToAPInt().isOneValue(); + + // Check that vectors don't contain 1 + if (this->getType()->isVectorTy()) { + unsigned NumElts = this->getType()->getVectorNumElements(); + for (unsigned i = 0; i != NumElts; ++i) { + Constant *Elt = this->getAggregateElement(i); + if (!Elt || !Elt->isNotOneValue()) + return false; + } + return true; + } + + // It *may* contain 1, we can't tell. + return false; +} + bool Constant::isMinSignedValue() const { // Check for INT_MIN integers if (const ConstantInt *CI = dyn_cast(this)) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp index 8bc34825f8a7b1..5b71f9d9c2e39a 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAddSub.cpp @@ -1896,14 +1896,16 @@ Instruction *InstCombiner::visitSub(BinaryOperator &I) { Builder.CreateNot(Y, Y->getName() + ".not")); // 0 - (X sdiv C) -> (X sdiv -C) provided the negation doesn't overflow. - // TODO: This could be extended to match arbitrary vector constants. - const APInt *DivC; - if (match(Op0, m_Zero()) && match(Op1, m_SDiv(m_Value(X), m_APInt(DivC))) && - !DivC->isMinSignedValue() && *DivC != 1) { - Constant *NegDivC = ConstantInt::get(I.getType(), -(*DivC)); - Instruction *BO = BinaryOperator::CreateSDiv(X, NegDivC); - BO->setIsExact(cast(Op1)->isExact()); - return BO; + if (match(Op0, m_Zero())) { + Constant *Op11C; + if (match(Op1, m_SDiv(m_Value(X), m_Constant(Op11C))) && + !Op11C->containsUndefElement() && Op11C->isNotMinSignedValue() && + Op11C->isNotOneValue()) { + Instruction *BO = + BinaryOperator::CreateSDiv(X, ConstantExpr::getNeg(Op11C)); + BO->setIsExact(cast(Op1)->isExact()); + return BO; + } } // 0 - (X << Y) -> (-X << Y) when X is freely negatable. diff --git a/llvm/test/Transforms/InstCombine/div.ll b/llvm/test/Transforms/InstCombine/div.ll index 4c4308151e7f4d..a4c6139e21062e 100644 --- a/llvm/test/Transforms/InstCombine/div.ll +++ b/llvm/test/Transforms/InstCombine/div.ll @@ -522,8 +522,7 @@ define <2 x i8> @sdiv_negated_dividend_constant_divisor_vec_undef(<2 x i8> %x) { define <2 x i64> @sdiv_negated_dividend_constant_divisor_vec(<2 x i64> %x) { ; CHECK-LABEL: @sdiv_negated_dividend_constant_divisor_vec( -; CHECK-NEXT: [[DIV1:%.*]] = sdiv <2 x i64> [[X:%.*]], -; CHECK-NEXT: [[DIV:%.*]] = sub nsw <2 x i64> zeroinitializer, [[DIV1]] +; CHECK-NEXT: [[DIV:%.*]] = sdiv <2 x i64> [[X:%.*]], ; CHECK-NEXT: ret <2 x i64> [[DIV]] ; %neg = sub nsw <2 x i64> zeroinitializer, %x @@ -533,8 +532,7 @@ define <2 x i64> @sdiv_negated_dividend_constant_divisor_vec(<2 x i64> %x) { define <2 x i64> @sdiv_exact_negated_dividend_constant_divisor_vec(<2 x i64> %x) { ; CHECK-LABEL: @sdiv_exact_negated_dividend_constant_divisor_vec( -; CHECK-NEXT: [[DIV1:%.*]] = sdiv exact <2 x i64> [[X:%.*]], -; CHECK-NEXT: [[DIV:%.*]] = sub nsw <2 x i64> zeroinitializer, [[DIV1]] +; CHECK-NEXT: [[DIV:%.*]] = sdiv exact <2 x i64> [[X:%.*]], ; CHECK-NEXT: ret <2 x i64> [[DIV]] ; %neg = sub nsw <2 x i64> zeroinitializer, %x @@ -860,8 +858,7 @@ define i32 @test_exact_nsw_exact(i32 %x) { define <2 x i64> @test_exact_vec(<2 x i64> %x) { ; CHECK-LABEL: @test_exact_vec( -; CHECK-NEXT: [[DIV:%.*]] = sdiv exact <2 x i64> [[X:%.*]], -; CHECK-NEXT: [[NEG:%.*]] = sub nsw <2 x i64> zeroinitializer, [[DIV]] +; CHECK-NEXT: [[NEG:%.*]] = sdiv exact <2 x i64> [[X:%.*]], ; CHECK-NEXT: ret <2 x i64> [[NEG]] ; %div = sdiv exact <2 x i64> %x,