From 474f7639e3494d9605f4444b087f48e710fbb0d4 Mon Sep 17 00:00:00 2001 From: River Riddle Date: Wed, 26 Aug 2020 01:13:01 -0700 Subject: [PATCH 01/10] [mlir] Fix bug in block merging when the types of the operands differ The merging algorithm was previously not checking for type equivalence. Fixes PR47314 Differential Revision: https://reviews.llvm.org/D86594 --- mlir/lib/Transforms/Utils/RegionUtils.cpp | 3 +++ .../Transforms/canonicalize-block-merge.mlir | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/mlir/lib/Transforms/Utils/RegionUtils.cpp b/mlir/lib/Transforms/Utils/RegionUtils.cpp index d6ecbf23416a65..3d1aea83716986 100644 --- a/mlir/lib/Transforms/Utils/RegionUtils.cpp +++ b/mlir/lib/Transforms/Utils/RegionUtils.cpp @@ -497,6 +497,9 @@ LogicalResult BlockMergeCluster::addToCluster(BlockEquivalenceData &blockData) { Value rhsOperand = rhsOperands[operand]; if (lhsOperand == rhsOperand) continue; + // Check that the types of the operands match. + if (lhsOperand.getType() != rhsOperand.getType()) + return failure(); // Check that these uses are both external, or both internal. bool lhsIsInBlock = lhsOperand.getParentBlock() == leaderBlock; diff --git a/mlir/test/Transforms/canonicalize-block-merge.mlir b/mlir/test/Transforms/canonicalize-block-merge.mlir index fe028b8af1fcdd..607f6cafb9de8f 100644 --- a/mlir/test/Transforms/canonicalize-block-merge.mlir +++ b/mlir/test/Transforms/canonicalize-block-merge.mlir @@ -202,3 +202,25 @@ func @mismatch_loop(%cond : i1) { return } + +// Check that blocks are not merged if the types of the operands differ. + +// CHECK-LABEL: func @mismatch_operand_types( +func @mismatch_operand_types(%arg0 : i1, %arg1 : memref, %arg2 : memref) { + %c0_i32 = constant 0 : i32 + %true = constant true + br ^bb1 + +^bb1: + cond_br %arg0, ^bb2, ^bb3 + +^bb2: + // CHECK: store %{{.*}}, %{{.*}} : memref + store %c0_i32, %arg1[] : memref + br ^bb1 + +^bb3: + // CHECK: store %{{.*}}, %{{.*}} : memref + store %true, %arg2[] : memref + br ^bb1 +} From b7e3599a22a9fdea42bf40cae750d10a5fb56888 Mon Sep 17 00:00:00 2001 From: Jay Foad Date: Mon, 24 Aug 2020 13:24:21 +0100 Subject: [PATCH 02/10] [SelectionDAG] Handle non-power-of-2 bitwidths in expandROT Differential Revision: https://reviews.llvm.org/D86449 --- .../CodeGen/SelectionDAG/TargetLowering.cpp | 36 ++++++++++++------- llvm/test/CodeGen/WebAssembly/fshl.ll | 22 ++++++++++++ 2 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 llvm/test/CodeGen/WebAssembly/fshl.ll diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 60ade429f71f59..559bdbe9da2079 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -6247,12 +6247,9 @@ bool TargetLowering::expandROT(SDNode *Node, SDValue &Result, EVT ShVT = Op1.getValueType(); SDValue Zero = DAG.getConstant(0, DL, ShVT); - assert(isPowerOf2_32(EltSizeInBits) && EltSizeInBits > 1 && - "Expecting the type bitwidth to be a power of 2"); - // If a rotate in the other direction is supported, use it. unsigned RevRot = IsLeft ? ISD::ROTR : ISD::ROTL; - if (isOperationLegalOrCustom(RevRot, VT)) { + if (isOperationLegalOrCustom(RevRot, VT) && isPowerOf2_32(EltSizeInBits)) { SDValue Sub = DAG.getNode(ISD::SUB, DL, ShVT, Zero, Op1); Result = DAG.getNode(RevRot, DL, VT, Op0, Sub); return true; @@ -6265,18 +6262,31 @@ bool TargetLowering::expandROT(SDNode *Node, SDValue &Result, !isOperationLegalOrCustomOrPromote(ISD::AND, VT))) return false; - // Otherwise, - // (rotl x, c) -> (or (shl x, (and c, w-1)), (srl x, (and -c, w-1))) - // (rotr x, c) -> (or (srl x, (and c, w-1)), (shl x, (and -c, w-1))) - // unsigned ShOpc = IsLeft ? ISD::SHL : ISD::SRL; unsigned HsOpc = IsLeft ? ISD::SRL : ISD::SHL; SDValue BitWidthMinusOneC = DAG.getConstant(EltSizeInBits - 1, DL, ShVT); - SDValue NegOp1 = DAG.getNode(ISD::SUB, DL, ShVT, Zero, Op1); - SDValue And0 = DAG.getNode(ISD::AND, DL, ShVT, Op1, BitWidthMinusOneC); - SDValue And1 = DAG.getNode(ISD::AND, DL, ShVT, NegOp1, BitWidthMinusOneC); - Result = DAG.getNode(ISD::OR, DL, VT, DAG.getNode(ShOpc, DL, VT, Op0, And0), - DAG.getNode(HsOpc, DL, VT, Op0, And1)); + SDValue ShVal; + SDValue HsVal; + if (isPowerOf2_32(EltSizeInBits)) { + // (rotl x, c) -> x << (c & (w - 1)) | x >> (-c & (w - 1)) + // (rotr x, c) -> x >> (c & (w - 1)) | x << (-c & (w - 1)) + SDValue NegOp1 = DAG.getNode(ISD::SUB, DL, ShVT, Zero, Op1); + SDValue ShAmt = DAG.getNode(ISD::AND, DL, ShVT, Op1, BitWidthMinusOneC); + ShVal = DAG.getNode(ShOpc, DL, VT, Op0, ShAmt); + SDValue HsAmt = DAG.getNode(ISD::AND, DL, ShVT, NegOp1, BitWidthMinusOneC); + HsVal = DAG.getNode(HsOpc, DL, VT, Op0, HsAmt); + } else { + // (rotl x, c) -> x << (c % w) | x >> 1 >> (w - 1 - (c % w)) + // (rotr x, c) -> x >> (c % w) | x << 1 << (w - 1 - (c % w)) + SDValue BitWidthC = DAG.getConstant(EltSizeInBits, DL, ShVT); + SDValue ShAmt = DAG.getNode(ISD::UREM, DL, ShVT, Op1, BitWidthC); + ShVal = DAG.getNode(ShOpc, DL, VT, Op0, ShAmt); + SDValue HsAmt = DAG.getNode(ISD::SUB, DL, ShVT, BitWidthMinusOneC, ShAmt); + SDValue One = DAG.getConstant(1, DL, ShVT); + HsVal = + DAG.getNode(HsOpc, DL, VT, DAG.getNode(HsOpc, DL, VT, Op0, One), HsAmt); + } + Result = DAG.getNode(ISD::OR, DL, VT, ShVal, HsVal); return true; } diff --git a/llvm/test/CodeGen/WebAssembly/fshl.ll b/llvm/test/CodeGen/WebAssembly/fshl.ll new file mode 100644 index 00000000000000..fb14826ff9a463 --- /dev/null +++ b/llvm/test/CodeGen/WebAssembly/fshl.ll @@ -0,0 +1,22 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc < %s | FileCheck %s + +target triple = "wasm32-unknown-unknown" + +; From https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=25150 +define i33 @fshr_multi_use(i33 %a) { +; CHECK-LABEL: fshr_multi_use: +; CHECK: .functype fshr_multi_use (i64) -> (i64) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: local.get 0 +; CHECK-NEXT: i64.const 1 +; CHECK-NEXT: i64.shr_u +; CHECK-NEXT: i64.const 31 +; CHECK-NEXT: i64.and +; CHECK-NEXT: # fallthrough-return + %b = tail call i33 @llvm.fshr.i33(i33 %a, i33 %a, i33 1) + %e = and i33 %b, 31 + ret i33 %e +} + +declare i33 @llvm.fshr.i33(i33, i33, i33) From 7518006d75accd21325747430d6bced66b2c5ada Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Wed, 26 Aug 2020 10:22:04 +0200 Subject: [PATCH 03/10] [lldb] XFAIL TestMemoryHistory on Linux This test appears to have never worked on Linux but it seems none of the current bots ever ran this test as it required enabling compiler-rt (otherwise it would have just been skipped). This just copies over the XFAIL decorator that are already on all other sanitizer tests. --- lldb/test/API/functionalities/asan/TestMemoryHistory.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lldb/test/API/functionalities/asan/TestMemoryHistory.py b/lldb/test/API/functionalities/asan/TestMemoryHistory.py index 37c34984f43b21..0b8dc20f27c53b 100644 --- a/lldb/test/API/functionalities/asan/TestMemoryHistory.py +++ b/lldb/test/API/functionalities/asan/TestMemoryHistory.py @@ -15,6 +15,9 @@ class AsanTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) + @expectedFailureAll( + oslist=["linux"], + bugnumber="non-core functionality, need to reenable and fix later (DES 2014.11.07)") @skipIfFreeBSD # llvm.org/pr21136 runtimes not yet available by default @expectedFailureNetBSD @skipUnlessAddressSanitizer From b20a4e293c3b617d0890657b3c46edcc7410c8fd Mon Sep 17 00:00:00 2001 From: Jan Kratochvil Date: Wed, 26 Aug 2020 09:57:53 +0200 Subject: [PATCH 04/10] [Support] Speedup llvm-dwarfdump 3.9x Currently `strace llvm-dwarfdump x.debug >/tmp/file`: ioctl(1, TCGETS, 0x7ffd64d7f340) = -1 ENOTTY (Inappropriate ioctl for device) write(1, " DW_AT_decl_line\t(89)\n"..., 4096) = 4096 ioctl(1, TCGETS, 0x7ffd64d7f400) = -1 ENOTTY (Inappropriate ioctl for device) ioctl(1, TCGETS, 0x7ffd64d7f410) = -1 ENOTTY (Inappropriate ioctl for device) ioctl(1, TCGETS, 0x7ffd64d7f400) = -1 ENOTTY (Inappropriate ioctl for device) After this patch: write(1, "0000000000001102 \"strlen\")\n "..., 4096) = 4096 write(1, "site\n DW_AT_low"..., 4096) = 4096 write(1, "d53)\n\n0x000e4d4d: DW_TAG_G"..., 4096) = 4096 The same speedup can be achieved by `--color=0` but that is not much convenient. This implementation has been suggested by Joerg Sonnenberger. Differential Revision: https://reviews.llvm.org/D86406 --- llvm/include/llvm/Support/raw_ostream.h | 1 + llvm/lib/Support/raw_ostream.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/llvm/include/llvm/Support/raw_ostream.h b/llvm/include/llvm/Support/raw_ostream.h index e9c710d0f38fe5..cae57430baffb8 100644 --- a/llvm/include/llvm/Support/raw_ostream.h +++ b/llvm/include/llvm/Support/raw_ostream.h @@ -412,6 +412,7 @@ class raw_fd_ostream : public raw_pwrite_stream { int FD; bool ShouldClose; bool SupportsSeeking = false; + mutable Optional HasColors; #ifdef _WIN32 /// True if this fd refers to a Windows console device. Mintty and other diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp index 86c48993957a0f..83050c8574d9d6 100644 --- a/llvm/lib/Support/raw_ostream.cpp +++ b/llvm/lib/Support/raw_ostream.cpp @@ -858,7 +858,9 @@ bool raw_fd_ostream::is_displayed() const { } bool raw_fd_ostream::has_colors() const { - return sys::Process::FileDescriptorHasColors(FD); + if (!HasColors) + HasColors = sys::Process::FileDescriptorHasColors(FD); + return *HasColors; } Expected raw_fd_ostream::lock() { From 0aaa2acc4ca0389a79a46bca3dc5b9365c641d77 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Wed, 26 Aug 2020 10:50:31 +0200 Subject: [PATCH 05/10] [clangd] Compute the inactive code range for semantic highlighting. Differential Revision: https://reviews.llvm.org/D85635 --- .../clangd/SemanticHighlighting.cpp | 53 ++++++++++++++----- .../unittests/SemanticHighlightingTests.cpp | 24 ++++----- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp index fb1ef1e326b412..4e66a9bb4e857e 100644 --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -221,23 +221,51 @@ class HighlightingsBuilder { // the end of the Tokens). TokRef = TokRef.drop_front(Conflicting.size()); } - // Add tokens indicating lines skipped by the preprocessor. - for (const Range &R : AST.getMacros().SkippedRanges) { + const auto &SM = AST.getSourceManager(); + StringRef MainCode = SM.getBuffer(SM.getMainFileID())->getBuffer(); + + // Merge token stream with "inactive line" markers. + std::vector WithInactiveLines; + auto SortedSkippedRanges = AST.getMacros().SkippedRanges; + llvm::sort(SortedSkippedRanges); + auto It = NonConflicting.begin(); + for (const Range &R : SortedSkippedRanges) { // Create one token for each line in the skipped range, so it works // with line-based diffing. assert(R.start.line <= R.end.line); for (int Line = R.start.line; Line <= R.end.line; ++Line) { - // Don't bother computing the offset for the end of the line, just use - // zero. The client will treat this highlighting kind specially, and - // highlight the entire line visually (i.e. not just to where the text - // on the line ends, but to the end of the screen). - NonConflicting.push_back({HighlightingKind::InactiveCode, - {Position{Line, 0}, Position{Line, 0}}}); + // Copy tokens before the inactive line + for (; It != NonConflicting.end() && It->R.start.line < Line; ++It) + WithInactiveLines.push_back(std::move(*It)); + // Add a token for the inactive line itself. + auto StartOfLine = positionToOffset(MainCode, Position{Line, 0}); + if (StartOfLine) { + StringRef LineText = + MainCode.drop_front(*StartOfLine).take_until([](char C) { + return C == '\n'; + }); + WithInactiveLines.push_back( + {HighlightingKind::InactiveCode, + {Position{Line, 0}, + Position{Line, static_cast(lspLength(LineText))}}}); + } else { + elog("Failed to convert position to offset: {0}", + StartOfLine.takeError()); + } + + // Skip any other tokens on the inactive line. e.g. + // `#ifndef Foo` is considered as part of an inactive region when Foo is + // defined, and there is a Foo macro token. + // FIXME: we should reduce the scope of the inactive region to not + // include the directive itself. + while (It != NonConflicting.end() && It->R.start.line == Line) + ++It; } } - // Re-sort the tokens because that's what the diffing expects. - llvm::sort(NonConflicting); - return NonConflicting; + // Copy tokens after the last inactive line + for (; It != NonConflicting.end(); ++It) + WithInactiveLines.push_back(std::move(*It)); + return WithInactiveLines; } private: @@ -493,9 +521,6 @@ toSemanticTokens(llvm::ArrayRef Tokens) { std::vector Result; const HighlightingToken *Last = nullptr; for (const HighlightingToken &Tok : Tokens) { - // FIXME: support inactive code - we need to provide the actual bounds. - if (Tok.Kind == HighlightingKind::InactiveCode) - continue; Result.emplace_back(); SemanticToken &Out = Result.back(); // deltaStart/deltaLine are relative if possible. diff --git a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp index 06743080166b4a..232be6a7838034 100644 --- a/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp +++ b/clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp @@ -503,11 +503,11 @@ TEST(SemanticHighlighting, GetsCorrectTokens) { #define $Macro[[test]] #undef $Macro[[test]] -$InactiveCode[[]] #ifdef $Macro[[test]] -$InactiveCode[[]] #endif +$InactiveCode[[#ifdef test]] +$InactiveCode[[#endif]] -$InactiveCode[[]] #if defined($Macro[[test]]) -$InactiveCode[[]] #endif +$InactiveCode[[#if defined(test)]] +$InactiveCode[[#endif]] )cpp", R"cpp( struct $Class[[S]] { @@ -614,8 +614,8 @@ TEST(SemanticHighlighting, GetsCorrectTokens) { R"cpp( // Code in the preamble. // Inactive lines get an empty InactiveCode token at the beginning. -$InactiveCode[[]] #ifdef $Macro[[test]] -$InactiveCode[[]] #endif +$InactiveCode[[#ifdef test]] +$InactiveCode[[#endif]] // A declaration to cause the preamble to end. int $Variable[[EndPreamble]]; @@ -623,17 +623,17 @@ TEST(SemanticHighlighting, GetsCorrectTokens) { // Code after the preamble. // Code inside inactive blocks does not get regular highlightings // because it's not part of the AST. -$InactiveCode[[]] #ifdef $Macro[[test]] -$InactiveCode[[]] int Inactive2; -$InactiveCode[[]] #endif +$InactiveCode[[#ifdef test]] +$InactiveCode[[int Inactive2;]] +$InactiveCode[[#endif]] #ifndef $Macro[[test]] int $Variable[[Active1]]; #endif -$InactiveCode[[]] #ifdef $Macro[[test]] -$InactiveCode[[]] int Inactive3; -$InactiveCode[[]] #else +$InactiveCode[[#ifdef test]] +$InactiveCode[[int Inactive3;]] +$InactiveCode[[#else]] int $Variable[[Active2]]; #endif )cpp", From f22d27624b6532a5542b283de9ce586c72c6b846 Mon Sep 17 00:00:00 2001 From: Yang Zhihui Date: Wed, 26 Aug 2020 04:28:50 -0400 Subject: [PATCH 06/10] [NFC] Fix some spelling errors in clang Driver Options.td Differential Revision: https://reviews.llvm.org/D86427 --- clang/include/clang/Driver/Options.td | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 5da072ee4cc285..b5a158cd333607 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -698,7 +698,7 @@ def emit_ast : Flag<["-"], "emit-ast">, def emit_llvm : Flag<["-"], "emit-llvm">, Flags<[CC1Option]>, Group, HelpText<"Use the LLVM representation for assembler and object files">; def emit_interface_stubs : Flag<["-"], "emit-interface-stubs">, Flags<[CC1Option]>, Group, - HelpText<"Generate Inteface Stub Files.">; + HelpText<"Generate Interface Stub Files.">; def emit_merged_ifs : Flag<["-"], "emit-merged-ifs">, Flags<[CC1Option]>, Group, HelpText<"Generate Interface Stub Files, emit merged text not binary.">; @@ -1809,7 +1809,7 @@ def fstack_protector : Flag<["-"], "fstack-protector">, Group, "This uses a loose heuristic which considers functions vulnerable if they " "contain a char (or 8bit integer) array or constant sized calls to alloca " ", which are of greater size than ssp-buffer-size (default: 8 bytes). All " - "variable sized calls to alloca are considered vulnerable. A function with" + "variable sized calls to alloca are considered vulnerable. A function with " "a stack protector has a guard value added to the stack frame that is " "checked on function exit. The guard value must be positioned in the " "stack frame such that a buffer overflow from a vulnerable variable will " From 5f47d4456d192eaea8c56a2b4648023c8743c927 Mon Sep 17 00:00:00 2001 From: Sander de Smalen Date: Wed, 19 Aug 2020 11:06:51 +0100 Subject: [PATCH 07/10] [AArch64][SVE] Fix calculation restore point for SVE callee saves. This fixes an issue where the restore point of callee-saves in the function epilogues was incorrectly calculated when the basic block consisted of only a RET instruction. This caused dealloc instructions to be inserted in between the block of callee-save restore instructions, rather than before it. Reviewed By: paulwalker-arm Differential Revision: https://reviews.llvm.org/D86099 --- .../Target/AArch64/AArch64FrameLowering.cpp | 7 ++-- .../framelayout-sve-calleesaves-fix.mir | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/framelayout-sve-calleesaves-fix.mir diff --git a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp index 83653dcbb8cf7e..c6cc6e9e847183 100644 --- a/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -1694,11 +1694,10 @@ void AArch64FrameLowering::emitEpilogue(MachineFunction &MF, StackOffset DeallocateBefore = {}, DeallocateAfter = SVEStackSize; MachineBasicBlock::iterator RestoreBegin = LastPopI, RestoreEnd = LastPopI; if (int64_t CalleeSavedSize = AFI->getSVECalleeSavedStackSize()) { - RestoreBegin = std::prev(RestoreEnd);; - while (IsSVECalleeSave(RestoreBegin) && - RestoreBegin != MBB.begin()) + RestoreBegin = std::prev(RestoreEnd); + while (RestoreBegin != MBB.begin() && + IsSVECalleeSave(std::prev(RestoreBegin))) --RestoreBegin; - ++RestoreBegin; assert(IsSVECalleeSave(RestoreBegin) && IsSVECalleeSave(std::prev(RestoreEnd)) && "Unexpected instruction"); diff --git a/llvm/test/CodeGen/AArch64/framelayout-sve-calleesaves-fix.mir b/llvm/test/CodeGen/AArch64/framelayout-sve-calleesaves-fix.mir new file mode 100644 index 00000000000000..a3cbd39c6531fd --- /dev/null +++ b/llvm/test/CodeGen/AArch64/framelayout-sve-calleesaves-fix.mir @@ -0,0 +1,36 @@ +# NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +# RUN: llc -mattr=+sve -mtriple=aarch64-none-linux-gnu -start-before=prologepilog %s -o - | FileCheck %s + +--- | + define aarch64_sve_vector_pcs void @fix_restorepoint_p4() { entry: unreachable } + ; CHECK-LABEL: fix_restorepoint_p4: + ; CHECK: // %bb.0: // %entry + ; CHECK-NEXT: str x29, [sp, #-16]! // 8-byte Folded Spill + ; CHECK-NEXT: addvl sp, sp, #-2 + ; CHECK-NEXT: str p4, [sp, #7, mul vl] // 2-byte Folded Spill + ; CHECK-NEXT: str z8, [sp, #1, mul vl] // 16-byte Folded Spill + ; CHECK-NEXT: addvl sp, sp, #-1 + ; CHECK-NEXT: .cfi_escape 0x0f, 0x0c, 0x8f, 0x00, 0x11, 0x10, 0x22, 0x11, 0x18, 0x92, 0x2e, 0x00, 0x1e, 0x22 // sp + 16 + 24 * VG + ; CHECK-NEXT: .cfi_escape 0x10, 0x48, 0x0a, 0x11, 0x70, 0x22, 0x11, 0x78, 0x92, 0x2e, 0x00, 0x1e, 0x22 // $d8 @ cfa - 16 - 8 * VG + ; CHECK-NEXT: .cfi_offset w29, -16 + ; CHECK-NEXT: // implicit-def: $z8 + ; CHECK-NEXT: // implicit-def: $p4 + ; CHECK-NEXT: addvl sp, sp, #1 + ; CHECK-NEXT: ldr p4, [sp, #7, mul vl] // 2-byte Folded Reload + ; CHECK-NEXT: ldr z8, [sp, #1, mul vl] // 16-byte Folded Reload + ; CHECK-NEXT: addvl sp, sp, #2 + ; CHECK-NEXT: ldr x29, [sp], #16 // 8-byte Folded Reload + ; CHECK-NEXT: ret +... +name: fix_restorepoint_p4 +stack: + - { id: 0, stack-id: sve-vec, size: 16, alignment: 16 } +body: | + bb.0.entry: + $z8 = IMPLICIT_DEF + $p4 = IMPLICIT_DEF + B %bb.1 + + bb.1.entry: + RET_ReallyLR +--- From 20676cab1168c2c60982af85f42725955cbcd7b5 Mon Sep 17 00:00:00 2001 From: Nithin Vadukkumchery Rajendrakumar Date: Wed, 26 Aug 2020 11:22:55 +0200 Subject: [PATCH 08/10] [analyzer] Add modeling of assignment operator in smart ptr Summary: Support for 'std::unique_ptr>::operator=' in SmartPtrModeling Reviewers: NoQ, Szelethus, vsavchenko, xazax.hun Reviewed By: NoQ, vsavchenko, xazax.hun Subscribers: martong, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D86293 --- .../Checkers/SmartPtrModeling.cpp | 92 ++++++++++++++++++- .../Inputs/system-header-simulator-cxx.h | 1 + clang/test/Analysis/smart-ptr-text-output.cpp | 57 ++++++++++-- clang/test/Analysis/smart-ptr.cpp | 61 +++++++++++- 4 files changed, 196 insertions(+), 15 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp index 0b084accbfbe8f..c405ef12433a71 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -37,7 +37,7 @@ namespace { class SmartPtrModeling : public Checker { - bool isNullAfterMoveMethod(const CallEvent &Call) const; + bool isAssignOpMethod(const CallEvent &Call) const; public: // Whether the checker should model for null dereferences of smart pointers. @@ -57,6 +57,7 @@ class SmartPtrModeling void handleRelease(const CallEvent &Call, CheckerContext &C) const; void handleSwap(const CallEvent &Call, CheckerContext &C) const; void handleGet(const CallEvent &Call, CheckerContext &C) const; + bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; @@ -123,7 +124,7 @@ static ProgramStateRef updateSwappedRegion(ProgramStateRef State, return State; } -bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { +bool SmartPtrModeling::isAssignOpMethod(const CallEvent &Call) const { // TODO: Update CallDescription to support anonymous calls? // TODO: Handle other methods, such as .get() or .release(). // But once we do, we'd need a visitor to explain null dereferences @@ -134,12 +135,11 @@ bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = C.getState(); if (!smartptr::isStdSmartPtrCall(Call)) return false; - if (isNullAfterMoveMethod(Call)) { + if (isAssignOpMethod(Call)) { const MemRegion *ThisR = cast(&Call)->getCXXThisVal().getAsRegion(); @@ -206,6 +206,9 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, return true; } + if (handleAssignOp(Call, C)) + return true; + const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); if (!Handler) return false; @@ -374,6 +377,87 @@ void SmartPtrModeling::handleGet(const CallEvent &Call, C.addTransition(State); } +bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto *OC = dyn_cast(&Call); + if (!OC) + return false; + OverloadedOperatorKind OOK = OC->getOverloadedOperator(); + if (OOK != OO_Equal) + return false; + const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return false; + + const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion(); + // In case of 'nullptr' or '0' assigned + if (!OtherSmartPtrRegion) { + bool AssignedNull = Call.getArgSVal(0).isZeroConstant(); + if (!AssignedNull) + return false; + auto NullVal = C.getSValBuilder().makeNull(); + State = State->set(ThisRegion, NullVal); + C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(ThisRegion)) + return; + OS << "Smart pointer "; + ThisRegion->printPretty(OS); + OS << " is assigned to null"; + })); + return true; + } + + const auto *OtherInnerPtr = State->get(OtherSmartPtrRegion); + if (OtherInnerPtr) { + State = State->set(ThisRegion, *OtherInnerPtr); + auto NullVal = C.getSValBuilder().makeNull(); + State = State->set(OtherSmartPtrRegion, NullVal); + bool IsArgValNull = OtherInnerPtr->isZeroConstant(); + + C.addTransition( + State, + C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull]( + PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) + return; + if (BR.isInteresting(OtherSmartPtrRegion)) { + OS << "Smart pointer "; + OtherSmartPtrRegion->printPretty(OS); + OS << " is null after being moved to "; + ThisRegion->printPretty(OS); + } + if (BR.isInteresting(ThisRegion) && IsArgValNull) { + OS << "Null pointer value move-assigned to "; + ThisRegion->printPretty(OS); + BR.markInteresting(OtherSmartPtrRegion); + } + })); + return true; + } else { + // In case we dont know anything about value we are moving from + // remove the entry from map for which smart pointer got moved to. + auto NullVal = C.getSValBuilder().makeNull(); + State = State->remove(ThisRegion); + State = State->set(OtherSmartPtrRegion, NullVal); + C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, + ThisRegion](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || + !BR.isInteresting(OtherSmartPtrRegion)) + return; + OS << "Smart pointer "; + OtherSmartPtrRegion->printPretty(OS); + OS << " is null after; previous value moved to "; + ThisRegion->printPretty(OS); + })); + return true; + } + return false; +} + void ento::registerSmartPtrModeling(CheckerManager &Mgr) { auto *Checker = Mgr.registerChecker(); Checker->ModelSmartPtrDereference = diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h index a0759479bfebf8..f2b148cbc692b4 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator-cxx.h +++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx.h @@ -970,6 +970,7 @@ class unique_ptr { T *operator->() const noexcept; operator bool() const noexcept; unique_ptr &operator=(unique_ptr &&p) noexcept; + unique_ptr &operator=(nullptr_t) noexcept; }; // TODO :: Once the deleter parameter is added update with additional template parameter. diff --git a/clang/test/Analysis/smart-ptr-text-output.cpp b/clang/test/Analysis/smart-ptr-text-output.cpp index 9af6f251e01d21..d63cd9b805f87e 100644 --- a/clang/test/Analysis/smart-ptr-text-output.cpp +++ b/clang/test/Analysis/smart-ptr-text-output.cpp @@ -80,7 +80,7 @@ void derefOnSwappedNullPtr() { void derefOnStdSwappedNullPtr() { std::unique_ptr P; // expected-note {{Default constructed smart pointer 'P' is null}} std::unique_ptr PNull; // expected-note {{Default constructed smart pointer 'PNull' is null}} - std::swap(P, PNull); // expected-note@Inputs/system-header-simulator-cxx.h:978 {{Swapped null smart pointer 'PNull' with smart pointer 'P'}} + std::swap(P, PNull); // expected-note@Inputs/system-header-simulator-cxx.h:979 {{Swapped null smart pointer 'PNull' with smart pointer 'P'}} // expected-note@-1 {{Calling 'swap'}} // expected-note@-2 {{Returning from 'swap'}} P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} @@ -109,14 +109,6 @@ void noNoteTagsForNonInterestingRegion() { // expected-note@-1{{Dereference of null smart pointer 'P'}} } -void noNoteTagsForNonMatchingBugType() { - std::unique_ptr P; // No note. - std::unique_ptr P1; // No note. - P1 = std::move(P); // expected-note {{Smart pointer 'P' of type 'std::unique_ptr' is reset to null when moved from}} - P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [cplusplus.Move]}} - // expected-note@-1 {{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} -} - void derefOnRawPtrFromGetOnNullPtr() { std::unique_ptr P; // FIXME: add note "Default constructed smart pointer 'P' is null" P.get()->foo(); // expected-warning {{Called C++ object pointer is null [core.CallAndMessage]}} @@ -131,3 +123,50 @@ void derefOnRawPtrFromGetOnValidPtr() { void derefOnRawPtrFromGetOnUnknownPtr(std::unique_ptr P) { P.get()->foo(); // No warning. } + +void derefOnMovedFromValidPtr() { + std::unique_ptr PToMove(new A()); // expected-note {{Smart pointer 'PToMove' is constructed}} + // FIXME: above note should go away once we fix marking region not interested. + std::unique_ptr P; + P = std::move(PToMove); // expected-note {{Smart pointer 'PToMove' is null after being moved to 'P'}} + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1 {{Dereference of null smart pointer 'PToMove'}} +} + +void derefOnMovedToNullPtr() { + std::unique_ptr PToMove(new A()); + std::unique_ptr P; + P = std::move(PToMove); // No note. + P->foo(); // No warning. +} + +void derefOnNullPtrGotMovedFromValidPtr() { + std::unique_ptr P(new A()); // expected-note {{Smart pointer 'P' is constructed}} + // FIXME: above note should go away once we fix marking region not interested. + std::unique_ptr PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}} + P = std::move(PToMove); // expected-note {{Null pointer value move-assigned to 'P'}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1 {{Dereference of null smart pointer 'P'}} +} + +void derefOnMovedUnknownPtr(std::unique_ptr PToMove) { + std::unique_ptr P; + P = std::move(PToMove); // expected-note {{Smart pointer 'PToMove' is null after; previous value moved to 'P'}} + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1 {{Dereference of null smart pointer 'PToMove'}} +} + +void derefOnAssignedNullPtrToNullSmartPtr() { + std::unique_ptr P; // expected-note {{Default constructed smart pointer 'P' is null}} + P = nullptr; // expected-note {{Smart pointer 'P' is assigned to null}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1 {{Dereference of null smart pointer 'P'}} +} + +void derefOnAssignedZeroToNullSmartPtr() { + std::unique_ptr P(new A()); // expected-note {{Smart pointer 'P' is constructed}} + // FIXME: above note should go away once we fix marking region not interested. + P = 0; // expected-note {{Smart pointer 'P' is assigned to null}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1 {{Dereference of null smart pointer 'P'}} +} diff --git a/clang/test/Analysis/smart-ptr.cpp b/clang/test/Analysis/smart-ptr.cpp index 17f6718c660578..1403cd6492b2b0 100644 --- a/clang/test/Analysis/smart-ptr.cpp +++ b/clang/test/Analysis/smart-ptr.cpp @@ -216,8 +216,7 @@ void derefAfterAssignment() { std::unique_ptr P; std::unique_ptr Q; Q = std::move(P); - // TODO: Fix test with expecting warning after '=' operator overloading modeling. - Q->foo(); // no-warning + Q->foo(); // expected-warning {{Dereference of null smart pointer 'Q' [alpha.cplusplus.SmartPtr]}} } } @@ -276,3 +275,61 @@ void derefOnRawPtrFromMultipleGetOnUnknownPtr(std::unique_ptr P) { Y->foo(); // expected-warning {{Called C++ object pointer is null [core.CallAndMessage]}} } } + +void derefOnMovedFromValidPtr() { + std::unique_ptr PToMove(new A()); + std::unique_ptr P; + P = std::move(PToMove); + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} +} + +void derefOnMovedToNullPtr() { + std::unique_ptr PToMove(new A()); + std::unique_ptr P; + P = std::move(PToMove); // No note. + P->foo(); // No warning. +} + +void derefOnNullPtrGotMovedFromValidPtr() { + std::unique_ptr P(new A()); + std::unique_ptr PToMove; + P = std::move(PToMove); + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +void derefOnMovedFromUnknownPtr(std::unique_ptr PToMove) { + std::unique_ptr P; + P = std::move(PToMove); + P->foo(); // No warning. +} + +void derefOnMovedUnknownPtr(std::unique_ptr PToMove) { + std::unique_ptr P; + P = std::move(PToMove); + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} +} + +void derefOnAssignedNullPtrToNullSmartPtr() { + std::unique_ptr P; + P = nullptr; + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +void derefOnAssignedZeroToNullSmartPtr() { + std::unique_ptr P(new A()); + P = 0; + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +void derefOnAssignedNullToUnknowSmartPtr(std::unique_ptr P) { + P = nullptr; + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +std::unique_ptr &&returnRValRefOfUniquePtr(); + +void drefOnAssignedNullFromMethodPtrValidSmartPtr() { + std::unique_ptr P(new A()); + P = returnRValRefOfUniquePtr(); + P->foo(); // No warning. +} From 1f44dfb640cbf04fd348c726950e556883cf7944 Mon Sep 17 00:00:00 2001 From: Cullen Rhodes Date: Tue, 25 Aug 2020 12:27:15 +0000 Subject: [PATCH 09/10] [AArch64][AsmParser] Fix bug in operand printer The switch in AArch64Operand::print was changed in D45688 so the shift can be printed after printing the register. This is implemented with LLVM_FALLTHROUGH and was broken in D52485 when BTIHint was put between the register and shift operands. Reviewed By: ostannard Differential Revision: https://reviews.llvm.org/D86535 --- llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp index b8d375737e7eab..2fe43f9e80e0d0 100644 --- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp +++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp @@ -2071,14 +2071,14 @@ void AArch64Operand::print(raw_ostream &OS) const { case k_PSBHint: OS << getPSBHintName(); break; + case k_BTIHint: + OS << getBTIHintName(); + break; case k_Register: OS << ""; if (!getShiftExtendAmount() && !hasShiftExtendAmount()) break; LLVM_FALLTHROUGH; - case k_BTIHint: - OS << getBTIHintName(); - break; case k_ShiftExtend: OS << "<" << AArch64_AM::getShiftExtendName(getShiftExtendType()) << " #" << getShiftExtendAmount(); From 99d18f79646cf154fed1ffdb473afa8ebd943b07 Mon Sep 17 00:00:00 2001 From: sstefan1 Date: Wed, 26 Aug 2020 10:37:48 +0200 Subject: [PATCH 10/10] Reland [IR] Intrinsics default attributes and opt-out flag Intrinsic properties can now be set to default and applied to all intrinsics. If the attributes are not needed, the user can opt-out by setting the DisableDefaultAttributes flag to true. Differential Revision: https://reviews.llvm.org/D70365 --- llvm/include/llvm/IR/Intrinsics.td | 11 +- llvm/test/TableGen/intrin-side-effects.td | 7 +- llvm/test/TableGen/intrinsic-long-name.td | 6 +- .../test/TableGen/intrinsic-pointer-to-any.td | 6 +- llvm/test/TableGen/intrinsic-struct.td | 6 +- llvm/test/TableGen/intrinsic-varargs.td | 5 +- .../TableGen/searchabletables-intrinsic.td | 6 +- llvm/utils/TableGen/CodeGenIntrinsics.h | 9 +- llvm/utils/TableGen/CodeGenTarget.cpp | 155 ++++++++++-------- .../utils/TableGen/SearchableTableEmitter.cpp | 3 +- 10 files changed, 139 insertions(+), 75 deletions(-) diff --git a/llvm/include/llvm/IR/Intrinsics.td b/llvm/include/llvm/IR/Intrinsics.td index 3162d4bea52983..8ece1f8b1b1231 100644 --- a/llvm/include/llvm/IR/Intrinsics.td +++ b/llvm/include/llvm/IR/Intrinsics.td @@ -17,7 +17,9 @@ include "llvm/CodeGen/SDNodeProperties.td" // Properties we keep track of for intrinsics. //===----------------------------------------------------------------------===// -class IntrinsicProperty; +class IntrinsicProperty { + bit IsDefault = is_default; +} // Intr*Mem - Memory properties. If no property is set, the worst case // is assumed (it may read and write any memory it can get access to and it may @@ -331,7 +333,8 @@ class Intrinsic ret_types, list param_types = [], list intr_properties = [], string name = "", - list sd_properties = []> : SDPatternOperator { + list sd_properties = [], + bit disable_default_attributes = 0> : SDPatternOperator { string LLVMName = name; string TargetPrefix = ""; // Set to a prefix for target-specific intrinsics. list RetTypes = ret_types; @@ -339,6 +342,10 @@ class Intrinsic ret_types, list IntrProperties = intr_properties; let Properties = sd_properties; + // Disable applying IntrinsicProperties that are marked default with + // IntrinsicProperty<1> + bit DisableDefaultAttributes = disable_default_attributes; + bit isTarget = 0; } diff --git a/llvm/test/TableGen/intrin-side-effects.td b/llvm/test/TableGen/intrin-side-effects.td index 7588855830fae2..f58d374532829f 100644 --- a/llvm/test/TableGen/intrin-side-effects.td +++ b/llvm/test/TableGen/intrin-side-effects.td @@ -11,7 +11,10 @@ class LLVMType { def llvm_i32_ty : LLVMType; -class IntrinsicProperty; +class IntrinsicProperty { + bit IsDefault = is_default; +} + def IntrNoMem : IntrinsicProperty; def IntrHasSideEffects : IntrinsicProperty; @@ -27,6 +30,8 @@ class Intrinsic ret_types, list ParamTypes = param_types; list IntrProperties = intr_properties; let Properties = sd_properties; + bit DisableDefaultAttributes = 1; + bit isTarget = 0; } diff --git a/llvm/test/TableGen/intrinsic-long-name.td b/llvm/test/TableGen/intrinsic-long-name.td index c2f696e8ca187b..d66173202302ba 100644 --- a/llvm/test/TableGen/intrinsic-long-name.td +++ b/llvm/test/TableGen/intrinsic-long-name.td @@ -1,7 +1,10 @@ // RUN: llvm-tblgen -gen-intrinsic-enums %s | FileCheck %s // XFAIL: vg_leak -class IntrinsicProperty; +class IntrinsicProperty { + bit IsDefault = is_default; +} + class SDNodeProperty; class ValueType { @@ -22,6 +25,7 @@ class Intrinsic param_types = []> { list ParamTypes = param_types; list IntrProperties = []; list Properties = []; + bit DisableDefaultAttributes = 1; } def iAny : ValueType<0, 253>; diff --git a/llvm/test/TableGen/intrinsic-pointer-to-any.td b/llvm/test/TableGen/intrinsic-pointer-to-any.td index c58595acfde773..0b0bc151077541 100644 --- a/llvm/test/TableGen/intrinsic-pointer-to-any.td +++ b/llvm/test/TableGen/intrinsic-pointer-to-any.td @@ -6,7 +6,10 @@ // case, so TableGen would hit an assertion in EncodeFixedType that was checking // to ensure that the substitution being processed was correctly replaced. -class IntrinsicProperty; +class IntrinsicProperty { + bit IsDefault = is_default; +} + class SDNodeProperty; class ValueType { @@ -32,6 +35,7 @@ class Intrinsic ret_types> { list IntrProperties = []; list Properties = []; bit isTarget = 0; + bit DisableDefaultAttributes = 1; } class LLVMQualPointerType diff --git a/llvm/test/TableGen/intrinsic-struct.td b/llvm/test/TableGen/intrinsic-struct.td index 7a3089c802a9c7..bc044a4a6f858e 100644 --- a/llvm/test/TableGen/intrinsic-struct.td +++ b/llvm/test/TableGen/intrinsic-struct.td @@ -1,7 +1,10 @@ // RUN: llvm-tblgen -gen-intrinsic-enums %s | FileCheck %s // XFAIL: vg_leak -class IntrinsicProperty; +class IntrinsicProperty { + bit IsDefault = is_default; +} + class SDNodeProperty; class ValueType { @@ -22,6 +25,7 @@ class Intrinsic ret_types = []> { list ParamTypes = []; list IntrProperties = []; list Properties = []; + bit DisableDefaultAttributes = 1; } def iAny : ValueType<0, 253>; diff --git a/llvm/test/TableGen/intrinsic-varargs.td b/llvm/test/TableGen/intrinsic-varargs.td index 6a2252215a830f..da860ed0129c8c 100644 --- a/llvm/test/TableGen/intrinsic-varargs.td +++ b/llvm/test/TableGen/intrinsic-varargs.td @@ -3,7 +3,9 @@ include "llvm/CodeGen/ValueTypes.td" -class IntrinsicProperty; +class IntrinsicProperty { + bit IsDefault = is_default; +} class SDNodeProperty; class LLVMType { @@ -18,6 +20,7 @@ class Intrinsic param_types = []> { list ParamTypes = param_types; list IntrProperties = []; list Properties = []; + bit DisableDefaultAttributes = 1; } def llvm_vararg_ty : LLVMType; // this means vararg here diff --git a/llvm/test/TableGen/searchabletables-intrinsic.td b/llvm/test/TableGen/searchabletables-intrinsic.td index e5cb9db3aa6be5..75722d19b16e99 100644 --- a/llvm/test/TableGen/searchabletables-intrinsic.td +++ b/llvm/test/TableGen/searchabletables-intrinsic.td @@ -3,7 +3,10 @@ include "llvm/TableGen/SearchableTable.td" -class IntrinsicProperty; +class IntrinsicProperty { + bit IsDefault = is_default; +} + class SDNodeProperty; class ValueType { @@ -24,6 +27,7 @@ class Intrinsic param_types = []> { list ParamTypes = param_types; list IntrProperties = []; list Properties = []; + bit DisableDefaultAttributes = 1; } def iAny : ValueType<0, 253>; diff --git a/llvm/utils/TableGen/CodeGenIntrinsics.h b/llvm/utils/TableGen/CodeGenIntrinsics.h index af59c1f3d83378..84ed0dc482fc24 100644 --- a/llvm/utils/TableGen/CodeGenIntrinsics.h +++ b/llvm/utils/TableGen/CodeGenIntrinsics.h @@ -176,6 +176,13 @@ struct CodeGenIntrinsic { return Properties & (1 << Prop); } + /// Goes through all IntrProperties that have IsDefault + /// value set and sets the property. + void setDefaultProperties(Record *R, std::vector DefaultProperties); + + /// Helper function to set property \p Name to true; + void setProperty(Record *R); + /// Returns true if the parameter at \p ParamIdx is a pointer type. Returns /// false if the parameter is not a pointer, or \p ParamIdx is greater than /// the size of \p IS.ParamVTs. @@ -185,7 +192,7 @@ struct CodeGenIntrinsic { bool isParamImmArg(unsigned ParamIdx) const; - CodeGenIntrinsic(Record *R); + CodeGenIntrinsic(Record *R, std::vector DefaultProperties); }; class CodeGenIntrinsicTable { diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp index 891a08ea590e2a..7824d8d1b34a0a 100644 --- a/llvm/utils/TableGen/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/CodeGenTarget.cpp @@ -599,12 +599,19 @@ ComplexPattern::ComplexPattern(Record *R) { //===----------------------------------------------------------------------===// CodeGenIntrinsicTable::CodeGenIntrinsicTable(const RecordKeeper &RC) { - std::vector Defs = RC.getAllDerivedDefinitions("Intrinsic"); + std::vector IntrProperties = + RC.getAllDerivedDefinitions("IntrinsicProperty"); + std::vector DefaultProperties; + for (Record *Rec : IntrProperties) + if (Rec->getValueAsBit("IsDefault")) + DefaultProperties.push_back(Rec); + + std::vector Defs = RC.getAllDerivedDefinitions("Intrinsic"); Intrinsics.reserve(Defs.size()); for (unsigned I = 0, e = Defs.size(); I != e; ++I) - Intrinsics.push_back(CodeGenIntrinsic(Defs[I])); + Intrinsics.push_back(CodeGenIntrinsic(Defs[I], DefaultProperties)); llvm::sort(Intrinsics, [](const CodeGenIntrinsic &LHS, const CodeGenIntrinsic &RHS) { @@ -620,7 +627,8 @@ CodeGenIntrinsicTable::CodeGenIntrinsicTable(const RecordKeeper &RC) { Targets.back().Count = Intrinsics.size() - Targets.back().Offset; } -CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { +CodeGenIntrinsic::CodeGenIntrinsic(Record *R, + std::vector DefaultProperties) { TheDef = R; std::string DefName = std::string(R->getName()); ArrayRef DefLoc = R->getLoc(); @@ -766,6 +774,9 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { IS.ParamTypeDefs.push_back(TyEl); } + // Set default properties to true. + setDefaultProperties(R, DefaultProperties); + // Parse the intrinsic properties. ListInit *PropList = R->getValueAsListInit("IntrProperties"); for (unsigned i = 0, e = PropList->size(); i != e; ++i) { @@ -773,68 +784,7 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { assert(Property->isSubClassOf("IntrinsicProperty") && "Expected a property!"); - if (Property->getName() == "IntrNoMem") - ModRef = NoMem; - else if (Property->getName() == "IntrReadMem") - ModRef = ModRefBehavior(ModRef & ~MR_Mod); - else if (Property->getName() == "IntrWriteMem") - ModRef = ModRefBehavior(ModRef & ~MR_Ref); - else if (Property->getName() == "IntrArgMemOnly") - ModRef = ModRefBehavior((ModRef & ~MR_Anywhere) | MR_ArgMem); - else if (Property->getName() == "IntrInaccessibleMemOnly") - ModRef = ModRefBehavior((ModRef & ~MR_Anywhere) | MR_InaccessibleMem); - else if (Property->getName() == "IntrInaccessibleMemOrArgMemOnly") - ModRef = ModRefBehavior((ModRef & ~MR_Anywhere) | MR_ArgMem | - MR_InaccessibleMem); - else if (Property->getName() == "Commutative") - isCommutative = true; - else if (Property->getName() == "Throws") - canThrow = true; - else if (Property->getName() == "IntrNoDuplicate") - isNoDuplicate = true; - else if (Property->getName() == "IntrConvergent") - isConvergent = true; - else if (Property->getName() == "IntrNoReturn") - isNoReturn = true; - else if (Property->getName() == "IntrNoSync") - isNoSync = true; - else if (Property->getName() == "IntrNoFree") - isNoFree = true; - else if (Property->getName() == "IntrWillReturn") - isWillReturn = true; - else if (Property->getName() == "IntrCold") - isCold = true; - else if (Property->getName() == "IntrSpeculatable") - isSpeculatable = true; - else if (Property->getName() == "IntrHasSideEffects") - hasSideEffects = true; - else if (Property->isSubClassOf("NoCapture")) { - unsigned ArgNo = Property->getValueAsInt("ArgNo"); - ArgumentAttributes.emplace_back(ArgNo, NoCapture, 0); - } else if (Property->isSubClassOf("NoAlias")) { - unsigned ArgNo = Property->getValueAsInt("ArgNo"); - ArgumentAttributes.emplace_back(ArgNo, NoAlias, 0); - } else if (Property->isSubClassOf("Returned")) { - unsigned ArgNo = Property->getValueAsInt("ArgNo"); - ArgumentAttributes.emplace_back(ArgNo, Returned, 0); - } else if (Property->isSubClassOf("ReadOnly")) { - unsigned ArgNo = Property->getValueAsInt("ArgNo"); - ArgumentAttributes.emplace_back(ArgNo, ReadOnly, 0); - } else if (Property->isSubClassOf("WriteOnly")) { - unsigned ArgNo = Property->getValueAsInt("ArgNo"); - ArgumentAttributes.emplace_back(ArgNo, WriteOnly, 0); - } else if (Property->isSubClassOf("ReadNone")) { - unsigned ArgNo = Property->getValueAsInt("ArgNo"); - ArgumentAttributes.emplace_back(ArgNo, ReadNone, 0); - } else if (Property->isSubClassOf("ImmArg")) { - unsigned ArgNo = Property->getValueAsInt("ArgNo"); - ArgumentAttributes.emplace_back(ArgNo, ImmArg, 0); - } else if (Property->isSubClassOf("Align")) { - unsigned ArgNo = Property->getValueAsInt("ArgNo"); - uint64_t Align = Property->getValueAsInt("Align"); - ArgumentAttributes.emplace_back(ArgNo, Alignment, Align); - } else - llvm_unreachable("Unknown property!"); + setProperty(Property); } // Also record the SDPatternOperator Properties. @@ -844,6 +794,81 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) { llvm::sort(ArgumentAttributes); } +void CodeGenIntrinsic::setDefaultProperties( + Record *R, std::vector DefaultProperties) { + // opt-out of using default attributes. + if (R->getValueAsBit("DisableDefaultAttributes")) + return; + + for (Record *Rec : DefaultProperties) + setProperty(Rec); +} + +void CodeGenIntrinsic::setProperty(Record *R) { + if (R->getName() == "IntrNoMem") + ModRef = NoMem; + else if (R->getName() == "IntrReadMem") + ModRef = ModRefBehavior(ModRef & ~MR_Mod); + else if (R->getName() == "IntrWriteMem") + ModRef = ModRefBehavior(ModRef & ~MR_Ref); + else if (R->getName() == "IntrArgMemOnly") + ModRef = ModRefBehavior((ModRef & ~MR_Anywhere) | MR_ArgMem); + else if (R->getName() == "IntrInaccessibleMemOnly") + ModRef = ModRefBehavior((ModRef & ~MR_Anywhere) | MR_InaccessibleMem); + else if (R->getName() == "IntrInaccessibleMemOrArgMemOnly") + ModRef = ModRefBehavior((ModRef & ~MR_Anywhere) | MR_ArgMem | + MR_InaccessibleMem); + else if (R->getName() == "Commutative") + isCommutative = true; + else if (R->getName() == "Throws") + canThrow = true; + else if (R->getName() == "IntrNoDuplicate") + isNoDuplicate = true; + else if (R->getName() == "IntrConvergent") + isConvergent = true; + else if (R->getName() == "IntrNoReturn") + isNoReturn = true; + else if (R->getName() == "IntrNoSync") + isNoSync = true; + else if (R->getName() == "IntrNoFree") + isNoFree = true; + else if (R->getName() == "IntrWillReturn") + isWillReturn = true; + else if (R->getName() == "IntrCold") + isCold = true; + else if (R->getName() == "IntrSpeculatable") + isSpeculatable = true; + else if (R->getName() == "IntrHasSideEffects") + hasSideEffects = true; + else if (R->isSubClassOf("NoCapture")) { + unsigned ArgNo = R->getValueAsInt("ArgNo"); + ArgumentAttributes.emplace_back(ArgNo, NoCapture, 0); + } else if (R->isSubClassOf("NoAlias")) { + unsigned ArgNo = R->getValueAsInt("ArgNo"); + ArgumentAttributes.emplace_back(ArgNo, NoAlias, 0); + } else if (R->isSubClassOf("Returned")) { + unsigned ArgNo = R->getValueAsInt("ArgNo"); + ArgumentAttributes.emplace_back(ArgNo, Returned, 0); + } else if (R->isSubClassOf("ReadOnly")) { + unsigned ArgNo = R->getValueAsInt("ArgNo"); + ArgumentAttributes.emplace_back(ArgNo, ReadOnly, 0); + } else if (R->isSubClassOf("WriteOnly")) { + unsigned ArgNo = R->getValueAsInt("ArgNo"); + ArgumentAttributes.emplace_back(ArgNo, WriteOnly, 0); + } else if (R->isSubClassOf("ReadNone")) { + unsigned ArgNo = R->getValueAsInt("ArgNo"); + ArgumentAttributes.emplace_back(ArgNo, ReadNone, 0); + } else if (R->isSubClassOf("ImmArg")) { + unsigned ArgNo = R->getValueAsInt("ArgNo"); + ArgumentAttributes.emplace_back(ArgNo, ImmArg, 0); + } else if (R->isSubClassOf("Align")) { + unsigned ArgNo = R->getValueAsInt("ArgNo"); + uint64_t Align = R->getValueAsInt("Align"); + ArgumentAttributes.emplace_back(ArgNo, Alignment, Align); + } else + llvm_unreachable("Unknown property!"); +} + bool CodeGenIntrinsic::isParamAPointer(unsigned ParamIdx) const { if (ParamIdx >= IS.ParamVTs.size()) return false; diff --git a/llvm/utils/TableGen/SearchableTableEmitter.cpp b/llvm/utils/TableGen/SearchableTableEmitter.cpp index 326cb4e54edce7..d092daf0d1f5da 100644 --- a/llvm/utils/TableGen/SearchableTableEmitter.cpp +++ b/llvm/utils/TableGen/SearchableTableEmitter.cpp @@ -138,7 +138,8 @@ class SearchableTableEmitter { CodeGenIntrinsic &getIntrinsic(Init *I) { std::unique_ptr &Intr = Intrinsics[I]; if (!Intr) - Intr = std::make_unique(cast(I)->getDef()); + Intr = std::make_unique(cast(I)->getDef(), + std::vector()); return *Intr; }