From b8880f5f97bf1628b2c9606e96abcd612dc7d747 Mon Sep 17 00:00:00 2001 From: Aart Bik Date: Fri, 25 Sep 2020 03:32:05 -0700 Subject: [PATCH 1/8] [mlir] [VectorOps] generalize printing support for integers This generalizes printing beyond just i1,i32,i64 and also accounts for signed and unsigned interpretation in the output. Reviewed By: nicolasvasilache Differential Revision: https://reviews.llvm.org/D88290 --- .../Dialect/Vector/CPU/test-print-int.mlir | 76 +++++++++++++ .../VectorToLLVM/ConvertVectorToLLVM.cpp | 103 ++++++++++++++---- mlir/lib/ExecutionEngine/CRunnerUtils.cpp | 2 + .../VectorToLLVM/vector-to-llvm.mlir | 85 ++++++++++++++- 4 files changed, 242 insertions(+), 24 deletions(-) create mode 100644 mlir/integration_test/Dialect/Vector/CPU/test-print-int.mlir diff --git a/mlir/integration_test/Dialect/Vector/CPU/test-print-int.mlir b/mlir/integration_test/Dialect/Vector/CPU/test-print-int.mlir new file mode 100644 index 00000000000000..946f02b0e3b915 --- /dev/null +++ b/mlir/integration_test/Dialect/Vector/CPU/test-print-int.mlir @@ -0,0 +1,76 @@ +// RUN: mlir-opt %s -convert-scf-to-std -convert-vector-to-llvm -convert-std-to-llvm | \ +// RUN: mlir-cpu-runner -e entry -entry-point-result=void \ +// RUN: -shared-libs=%mlir_integration_test_dir/libmlir_c_runner_utils%shlibext | \ +// RUN: FileCheck %s + +// +// Test various signless, signed, unsigned integer types. +// +func @entry() { + %0 = std.constant dense<[true, false, -1, 0, 1]> : vector<5xi1> + vector.print %0 : vector<5xi1> + // CHECK: ( 1, 0, 1, 0, 1 ) + + %1 = std.constant dense<[true, false, -1, 0]> : vector<4xsi1> + vector.print %1 : vector<4xsi1> + // CHECK: ( 1, 0, 1, 0 ) + + %2 = std.constant dense<[true, false, 0, 1]> : vector<4xui1> + vector.print %2 : vector<4xui1> + // CHECK: ( 1, 0, 0, 1 ) + + %3 = std.constant dense<[-128, -127, -1, 0, 1, 127, 128, 254, 255]> : vector<9xi8> + vector.print %3 : vector<9xi8> + // CHECK: ( -128, -127, -1, 0, 1, 127, -128, -2, -1 ) + + %4 = std.constant dense<[-128, -127, -1, 0, 1, 127]> : vector<6xsi8> + vector.print %4 : vector<6xsi8> + // CHECK: ( -128, -127, -1, 0, 1, 127 ) + + %5 = std.constant dense<[0, 1, 127, 128, 254, 255]> : vector<6xui8> + vector.print %5 : vector<6xui8> + // CHECK: ( 0, 1, 127, 128, 254, 255 ) + + %6 = std.constant dense<[-32768, -32767, -1, 0, 1, 32767, 32768, 65534, 65535]> : vector<9xi16> + vector.print %6 : vector<9xi16> + // CHECK: ( -32768, -32767, -1, 0, 1, 32767, -32768, -2, -1 ) + + %7 = std.constant dense<[-32768, -32767, -1, 0, 1, 32767]> : vector<6xsi16> + vector.print %7 : vector<6xsi16> + // CHECK: ( -32768, -32767, -1, 0, 1, 32767 ) + + %8 = std.constant dense<[0, 1, 32767, 32768, 65534, 65535]> : vector<6xui16> + vector.print %8 : vector<6xui16> + // CHECK: ( 0, 1, 32767, 32768, 65534, 65535 ) + + %9 = std.constant dense<[-2147483648, -2147483647, -1, 0, 1, + 2147483647, 2147483648, 4294967294, 4294967295]> : vector<9xi32> + vector.print %9 : vector<9xi32> + // CHECK: ( -2147483648, -2147483647, -1, 0, 1, 2147483647, -2147483648, -2, -1 ) + + %10 = std.constant dense<[-2147483648, -2147483647, -1, 0, 1, 2147483647]> : vector<6xsi32> + vector.print %10 : vector<6xsi32> + // CHECK: ( -2147483648, -2147483647, -1, 0, 1, 2147483647 ) + + %11 = std.constant dense<[0, 1, 2147483647, 2147483648, 4294967294, 4294967295]> : vector<6xui32> + vector.print %11 : vector<6xui32> + // CHECK: ( 0, 1, 2147483647, 2147483648, 4294967294, 4294967295 ) + + %12 = std.constant dense<[-9223372036854775808, -9223372036854775807, -1, 0, 1, + 9223372036854775807, 9223372036854775808, + 18446744073709551614, 18446744073709551615]> : vector<9xi64> + vector.print %12 : vector<9xi64> + // CHECK: ( -9223372036854775808, -9223372036854775807, -1, 0, 1, 9223372036854775807, -9223372036854775808, -2, -1 ) + + %13 = std.constant dense<[-9223372036854775808, -9223372036854775807, -1, 0, 1, + 9223372036854775807]> : vector<6xsi64> + vector.print %13 : vector<6xsi64> + // CHECK: ( -9223372036854775808, -9223372036854775807, -1, 0, 1, 9223372036854775807 ) + + %14 = std.constant dense<[0, 1, 9223372036854775807, 9223372036854775808, + 18446744073709551614, 18446744073709551615]> : vector<6xui64> + vector.print %14 : vector<6xui64> + // CHECK: ( 0, 1, 9223372036854775807, 9223372036854775808, 18446744073709551614, 18446744073709551615 ) + + return +} diff --git a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp index 6ad17d77069c15..b48b435d0278b3 100644 --- a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp +++ b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp @@ -1319,44 +1319,96 @@ class VectorPrintOpConversion : public ConvertToLLVMPattern { if (typeConverter.convertType(printType) == nullptr) return failure(); - // Make sure element type has runtime support (currently just Float/Double). + // Make sure element type has runtime support. + PrintConversion conversion = PrintConversion::None; VectorType vectorType = printType.dyn_cast(); Type eltType = vectorType ? vectorType.getElementType() : printType; - int64_t rank = vectorType ? vectorType.getRank() : 0; Operation *printer; - if (eltType.isSignlessInteger(1) || eltType.isSignlessInteger(32)) - printer = getPrintI32(op); - else if (eltType.isSignlessInteger(64)) - printer = getPrintI64(op); - else if (eltType.isF32()) + if (eltType.isF32()) { printer = getPrintFloat(op); - else if (eltType.isF64()) + } else if (eltType.isF64()) { printer = getPrintDouble(op); - else + } else if (auto intTy = eltType.dyn_cast()) { + // Integers need a zero or sign extension on the operand + // (depending on the source type) as well as a signed or + // unsigned print method. Up to 64-bit is supported. + unsigned width = intTy.getWidth(); + if (intTy.isUnsigned()) { + if (width <= 32) { + if (width < 32) + conversion = PrintConversion::ZeroExt32; + printer = getPrintU32(op); + } else if (width <= 64) { + if (width < 64) + conversion = PrintConversion::ZeroExt64; + printer = getPrintU64(op); + } else { + return failure(); + } + } else { + assert(intTy.isSignless() || intTy.isSigned()); + if (width <= 32) { + // Note that we *always* zero extend booleans (1-bit integers), + // so that true/false is printed as 1/0 rather than -1/0. + if (width == 1) + conversion = PrintConversion::ZeroExt32; + else if (width < 32) + conversion = PrintConversion::SignExt32; + printer = getPrintI32(op); + } else if (width <= 64) { + if (width < 64) + conversion = PrintConversion::SignExt64; + printer = getPrintI64(op); + } else { + return failure(); + } + } + } else { return failure(); + } // Unroll vector into elementary print calls. - emitRanks(rewriter, op, adaptor.source(), vectorType, printer, rank); + int64_t rank = vectorType ? vectorType.getRank() : 0; + emitRanks(rewriter, op, adaptor.source(), vectorType, printer, rank, + conversion); emitCall(rewriter, op->getLoc(), getPrintNewline(op)); rewriter.eraseOp(op); return success(); } private: + enum class PrintConversion { + None, + ZeroExt32, + SignExt32, + ZeroExt64, + SignExt64 + }; + void emitRanks(ConversionPatternRewriter &rewriter, Operation *op, Value value, VectorType vectorType, Operation *printer, - int64_t rank) const { + int64_t rank, PrintConversion conversion) const { Location loc = op->getLoc(); if (rank == 0) { - if (value.getType() == LLVM::LLVMType::getInt1Ty(rewriter.getContext())) { - // Convert i1 (bool) to i32 so we can use the print_i32 method. - // This avoids the need for a print_i1 method with an unclear ABI. - auto i32Type = LLVM::LLVMType::getInt32Ty(rewriter.getContext()); - auto trueVal = rewriter.create( - loc, i32Type, rewriter.getI32IntegerAttr(1)); - auto falseVal = rewriter.create( - loc, i32Type, rewriter.getI32IntegerAttr(0)); - value = rewriter.create(loc, value, trueVal, falseVal); + switch (conversion) { + case PrintConversion::ZeroExt32: + value = rewriter.create( + loc, value, LLVM::LLVMType::getInt32Ty(rewriter.getContext())); + break; + case PrintConversion::SignExt32: + value = rewriter.create( + loc, value, LLVM::LLVMType::getInt32Ty(rewriter.getContext())); + break; + case PrintConversion::ZeroExt64: + value = rewriter.create( + loc, value, LLVM::LLVMType::getInt64Ty(rewriter.getContext())); + break; + case PrintConversion::SignExt64: + value = rewriter.create( + loc, value, LLVM::LLVMType::getInt64Ty(rewriter.getContext())); + break; + case PrintConversion::None: + break; } emitCall(rewriter, loc, printer, value); return; @@ -1372,7 +1424,8 @@ class VectorPrintOpConversion : public ConvertToLLVMPattern { rank > 1 ? reducedType : vectorType.getElementType()); Value nestedVal = extractOne(rewriter, typeConverter, loc, value, llvmType, rank, d); - emitRanks(rewriter, op, nestedVal, reducedType, printer, rank - 1); + emitRanks(rewriter, op, nestedVal, reducedType, printer, rank - 1, + conversion); if (d != dim - 1) emitCall(rewriter, loc, printComma); } @@ -1410,6 +1463,14 @@ class VectorPrintOpConversion : public ConvertToLLVMPattern { return getPrint(op, "print_i64", LLVM::LLVMType::getInt64Ty(op->getContext())); } + Operation *getPrintU32(Operation *op) const { + return getPrint(op, "printU32", + LLVM::LLVMType::getInt32Ty(op->getContext())); + } + Operation *getPrintU64(Operation *op) const { + return getPrint(op, "printU64", + LLVM::LLVMType::getInt64Ty(op->getContext())); + } Operation *getPrintFloat(Operation *op) const { return getPrint(op, "print_f32", LLVM::LLVMType::getFloatTy(op->getContext())); diff --git a/mlir/lib/ExecutionEngine/CRunnerUtils.cpp b/mlir/lib/ExecutionEngine/CRunnerUtils.cpp index ad5be24378cebb..6efc48768a965f 100644 --- a/mlir/lib/ExecutionEngine/CRunnerUtils.cpp +++ b/mlir/lib/ExecutionEngine/CRunnerUtils.cpp @@ -25,6 +25,8 @@ // details of our vectors. Also useful for direct LLVM IR output. extern "C" void print_i32(int32_t i) { fprintf(stdout, "%" PRId32, i); } extern "C" void print_i64(int64_t l) { fprintf(stdout, "%" PRId64, l); } +extern "C" void printU32(uint32_t i) { fprintf(stdout, "%" PRIu32, i); } +extern "C" void printU64(uint64_t l) { fprintf(stdout, "%" PRIu64, l); } extern "C" void print_f32(float f) { fprintf(stdout, "%g", f); } extern "C" void print_f64(double d) { fprintf(stdout, "%lg", d); } extern "C" void print_open() { fputs("( ", stdout); } diff --git a/mlir/test/Conversion/VectorToLLVM/vector-to-llvm.mlir b/mlir/test/Conversion/VectorToLLVM/vector-to-llvm.mlir index 82db2c55c906db..d382c50f9132c8 100644 --- a/mlir/test/Conversion/VectorToLLVM/vector-to-llvm.mlir +++ b/mlir/test/Conversion/VectorToLLVM/vector-to-llvm.mlir @@ -433,14 +433,45 @@ func @vector_print_scalar_i1(%arg0: i1) { vector.print %arg0 : i1 return } +// +// Type "boolean" always uses zero extension. +// // CHECK-LABEL: llvm.func @vector_print_scalar_i1( // CHECK-SAME: %[[A:.*]]: !llvm.i1) -// CHECK: %[[T:.*]] = llvm.mlir.constant(1 : i32) : !llvm.i32 -// CHECK: %[[F:.*]] = llvm.mlir.constant(0 : i32) : !llvm.i32 -// CHECK: %[[S:.*]] = llvm.select %[[A]], %[[T]], %[[F]] : !llvm.i1, !llvm.i32 +// CHECK: %[[S:.*]] = llvm.zext %[[A]] : !llvm.i1 to !llvm.i32 +// CHECK: llvm.call @print_i32(%[[S]]) : (!llvm.i32) -> () +// CHECK: llvm.call @print_newline() : () -> () + +func @vector_print_scalar_i4(%arg0: i4) { + vector.print %arg0 : i4 + return +} +// CHECK-LABEL: llvm.func @vector_print_scalar_i4( +// CHECK-SAME: %[[A:.*]]: !llvm.i4) +// CHECK: %[[S:.*]] = llvm.sext %[[A]] : !llvm.i4 to !llvm.i32 // CHECK: llvm.call @print_i32(%[[S]]) : (!llvm.i32) -> () // CHECK: llvm.call @print_newline() : () -> () +func @vector_print_scalar_si4(%arg0: si4) { + vector.print %arg0 : si4 + return +} +// CHECK-LABEL: llvm.func @vector_print_scalar_si4( +// CHECK-SAME: %[[A:.*]]: !llvm.i4) +// CHECK: %[[S:.*]] = llvm.sext %[[A]] : !llvm.i4 to !llvm.i32 +// CHECK: llvm.call @print_i32(%[[S]]) : (!llvm.i32) -> () +// CHECK: llvm.call @print_newline() : () -> () + +func @vector_print_scalar_ui4(%arg0: ui4) { + vector.print %arg0 : ui4 + return +} +// CHECK-LABEL: llvm.func @vector_print_scalar_ui4( +// CHECK-SAME: %[[A:.*]]: !llvm.i4) +// CHECK: %[[S:.*]] = llvm.zext %[[A]] : !llvm.i4 to !llvm.i32 +// CHECK: llvm.call @printU32(%[[S]]) : (!llvm.i32) -> () +// CHECK: llvm.call @print_newline() : () -> () + func @vector_print_scalar_i32(%arg0: i32) { vector.print %arg0 : i32 return @@ -450,6 +481,45 @@ func @vector_print_scalar_i32(%arg0: i32) { // CHECK: llvm.call @print_i32(%[[A]]) : (!llvm.i32) -> () // CHECK: llvm.call @print_newline() : () -> () +func @vector_print_scalar_ui32(%arg0: ui32) { + vector.print %arg0 : ui32 + return +} +// CHECK-LABEL: llvm.func @vector_print_scalar_ui32( +// CHECK-SAME: %[[A:.*]]: !llvm.i32) +// CHECK: llvm.call @printU32(%[[A]]) : (!llvm.i32) -> () +// CHECK: llvm.call @print_newline() : () -> () + +func @vector_print_scalar_i40(%arg0: i40) { + vector.print %arg0 : i40 + return +} +// CHECK-LABEL: llvm.func @vector_print_scalar_i40( +// CHECK-SAME: %[[A:.*]]: !llvm.i40) +// CHECK: %[[S:.*]] = llvm.sext %[[A]] : !llvm.i40 to !llvm.i64 +// CHECK: llvm.call @print_i64(%[[S]]) : (!llvm.i64) -> () +// CHECK: llvm.call @print_newline() : () -> () + +func @vector_print_scalar_si40(%arg0: si40) { + vector.print %arg0 : si40 + return +} +// CHECK-LABEL: llvm.func @vector_print_scalar_si40( +// CHECK-SAME: %[[A:.*]]: !llvm.i40) +// CHECK: %[[S:.*]] = llvm.sext %[[A]] : !llvm.i40 to !llvm.i64 +// CHECK: llvm.call @print_i64(%[[S]]) : (!llvm.i64) -> () +// CHECK: llvm.call @print_newline() : () -> () + +func @vector_print_scalar_ui40(%arg0: ui40) { + vector.print %arg0 : ui40 + return +} +// CHECK-LABEL: llvm.func @vector_print_scalar_ui40( +// CHECK-SAME: %[[A:.*]]: !llvm.i40) +// CHECK: %[[S:.*]] = llvm.zext %[[A]] : !llvm.i40 to !llvm.i64 +// CHECK: llvm.call @printU64(%[[S]]) : (!llvm.i64) -> () +// CHECK: llvm.call @print_newline() : () -> () + func @vector_print_scalar_i64(%arg0: i64) { vector.print %arg0 : i64 return @@ -459,6 +529,15 @@ func @vector_print_scalar_i64(%arg0: i64) { // CHECK: llvm.call @print_i64(%[[A]]) : (!llvm.i64) -> () // CHECK: llvm.call @print_newline() : () -> () +func @vector_print_scalar_ui64(%arg0: ui64) { + vector.print %arg0 : ui64 + return +} +// CHECK-LABEL: llvm.func @vector_print_scalar_ui64( +// CHECK-SAME: %[[A:.*]]: !llvm.i64) +// CHECK: llvm.call @printU64(%[[A]]) : (!llvm.i64) -> () +// CHECK: llvm.call @print_newline() : () -> () + func @vector_print_scalar_f32(%arg0: f32) { vector.print %arg0 : f32 return From e336b74c995d665bc3fb75164375bbb0f78f516c Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Tue, 7 Jul 2020 12:17:14 +0200 Subject: [PATCH 2/8] [clang-format] Add a MacroExpander. Summary: The MacroExpander allows to expand simple (non-resursive) macro definitions from a macro identifier token and macro arguments. It annotates the tokens with a newly introduced MacroContext that keeps track of the role a token played in expanding the macro in order to be able to reconstruct the macro expansion from an expanded (formatted) token stream. Made Token explicitly copy-able to enable copying tokens from the parsed macro definition. Reviewers: sammccall Subscribers: mgorny, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D83296 --- clang/lib/Format/CMakeLists.txt | 1 + clang/lib/Format/FormatToken.h | 76 ++++++- clang/lib/Format/MacroExpander.cpp | 225 +++++++++++++++++++ clang/lib/Format/Macros.h | 141 ++++++++++++ clang/unittests/Format/CMakeLists.txt | 1 + clang/unittests/Format/MacroExpanderTest.cpp | 187 +++++++++++++++ clang/unittests/Format/TestLexer.h | 88 ++++++++ 7 files changed, 716 insertions(+), 3 deletions(-) create mode 100644 clang/lib/Format/MacroExpander.cpp create mode 100644 clang/lib/Format/Macros.h create mode 100644 clang/unittests/Format/MacroExpanderTest.cpp create mode 100644 clang/unittests/Format/TestLexer.h diff --git a/clang/lib/Format/CMakeLists.txt b/clang/lib/Format/CMakeLists.txt index 0019d045cd06d7..ec1522db7e870d 100644 --- a/clang/lib/Format/CMakeLists.txt +++ b/clang/lib/Format/CMakeLists.txt @@ -7,6 +7,7 @@ add_clang_library(clangFormat Format.cpp FormatToken.cpp FormatTokenLexer.cpp + MacroExpander.cpp NamespaceEndCommentsFixer.cpp SortJavaScriptImports.cpp TokenAnalyzer.cpp diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 76ef99e72d58ed..c6af71a768a1aa 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -136,6 +136,68 @@ enum ParameterPackingKind { PPK_BinPacked, PPK_OnePerLine, PPK_Inconclusive }; enum FormatDecision { FD_Unformatted, FD_Continue, FD_Break }; +/// Roles a token can take in a configured macro expansion. +enum MacroRole { + /// The token was expanded from a macro argument when formatting the expanded + /// token sequence. + MR_ExpandedArg, + /// The token is part of a macro argument that was previously formatted as + /// expansion when formatting the unexpanded macro call. + MR_UnexpandedArg, + /// The token was expanded from a macro definition, and is not visible as part + /// of the macro call. + MR_Hidden, +}; + +struct FormatToken; + +/// Contains information on the token's role in a macro expansion. +/// +/// Given the following definitions: +/// A(X) = [ X ] +/// B(X) = < X > +/// C(X) = X +/// +/// Consider the macro call: +/// A({B(C(C(x)))}) -> [{}] +/// +/// In this case, the tokens of the unexpanded macro call will have the +/// following relevant entries in their macro context (note that formatting +/// the unexpanded macro call happens *after* formatting the expanded macro +/// call): +/// A( { B( C( C(x) ) ) } ) +/// Role: NN U NN NN NNUN N N U N (N=None, U=UnexpandedArg) +/// +/// [ { < x > } ] +/// Role: H E H E H E H (H=Hidden, E=ExpandedArg) +/// ExpandedFrom[0]: A A A A A A A +/// ExpandedFrom[1]: B B B +/// ExpandedFrom[2]: C +/// ExpandedFrom[3]: C +/// StartOfExpansion: 1 0 1 2 0 0 0 +/// EndOfExpansion: 0 0 0 2 1 0 1 +struct MacroExpansion { + MacroExpansion(MacroRole Role) : Role(Role) {} + + /// The token's role in the macro expansion. + /// When formatting an expanded macro, all tokens that are part of macro + /// arguments will be MR_ExpandedArg, while all tokens that are not visible in + /// the macro call will be MR_Hidden. + /// When formatting an unexpanded macro call, all tokens that are part of + /// macro arguments will be MR_UnexpandedArg. + MacroRole Role; + + /// The stack of macro call identifier tokens this token was expanded from. + llvm::SmallVector ExpandedFrom; + + /// The number of expansions of which this macro is the first entry. + unsigned StartOfExpansion = 0; + + /// The number of currently open expansions in \c ExpandedFrom this macro is + /// the last token in. + unsigned EndOfExpansion = 0; +}; + class TokenRole; class AnnotatedLine; @@ -163,7 +225,9 @@ struct FormatToken { /// A token can have a special role that can carry extra information /// about the token's formatting. - std::unique_ptr Role; + /// FIXME: Make FormatToken for parsing and AnnotatedToken two different + /// classes and make this a unique_ptr in the AnnotatedToken class. + std::shared_ptr Role; /// The range of the whitespace immediately preceding the \c Token. SourceRange WhitespaceRange; @@ -378,6 +442,10 @@ struct FormatToken { /// in it. SmallVector Children; + // Contains all attributes related to how this token takes part + // in a configured macro expansion. + llvm::Optional MacroCtx; + bool is(tok::TokenKind Kind) const { return Tok.is(Kind); } bool is(TokenType TT) const { return getType() == TT; } bool is(const IdentifierInfo *II) const { @@ -631,10 +699,12 @@ struct FormatToken { : nullptr; } + void copyFrom(const FormatToken &Tok) { *this = Tok; } + private: - // Disallow copying. + // Only allow copying via the explicit copyFrom method. FormatToken(const FormatToken &) = delete; - void operator=(const FormatToken &) = delete; + FormatToken &operator=(const FormatToken &) = default; template bool startsSequenceInternal(A K1, Ts... Tokens) const { diff --git a/clang/lib/Format/MacroExpander.cpp b/clang/lib/Format/MacroExpander.cpp new file mode 100644 index 00000000000000..c00fc209deb567 --- /dev/null +++ b/clang/lib/Format/MacroExpander.cpp @@ -0,0 +1,225 @@ +//===--- MacroExpander.cpp - Format C++ code --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the implementation of MacroExpander, which handles macro +/// configuration and expansion while formatting. +/// +//===----------------------------------------------------------------------===// + +#include "Macros.h" + +#include "Encoding.h" +#include "FormatToken.h" +#include "FormatTokenLexer.h" +#include "clang/Basic/TokenKinds.h" +#include "clang/Format/Format.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/ModuleLoader.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/ErrorHandling.h" + +namespace clang { +namespace format { + +struct MacroExpander::Definition { + StringRef Name; + SmallVector Params; + SmallVector Body; + + // Map from each argument's name to its position in the argument list. + // With "M(x, y) x + y": + // x -> 0 + // y -> 1 + llvm::StringMap ArgMap; + + bool ObjectLike = true; +}; + +class MacroExpander::DefinitionParser { +public: + DefinitionParser(ArrayRef Tokens) : Tokens(Tokens) { + assert(!Tokens.empty()); + Current = Tokens[0]; + } + + // Parse the token stream and return the corresonding Definition object. + // Returns an empty definition object with a null-Name on error. + MacroExpander::Definition parse() { + if (!Current->is(tok::identifier)) + return {}; + Def.Name = Current->TokenText; + nextToken(); + if (Current->is(tok::l_paren)) { + Def.ObjectLike = false; + if (!parseParams()) + return {}; + } + if (!parseExpansion()) + return {}; + + return Def; + } + +private: + bool parseParams() { + assert(Current->is(tok::l_paren)); + nextToken(); + while (Current->is(tok::identifier)) { + Def.Params.push_back(Current); + Def.ArgMap[Def.Params.back()->TokenText] = Def.Params.size() - 1; + nextToken(); + if (Current->isNot(tok::comma)) + break; + nextToken(); + } + if (Current->isNot(tok::r_paren)) + return false; + nextToken(); + return true; + } + + bool parseExpansion() { + if (!Current->isOneOf(tok::equal, tok::eof)) + return false; + if (Current->is(tok::equal)) + nextToken(); + parseTail(); + return true; + } + + void parseTail() { + while (Current->isNot(tok::eof)) { + Def.Body.push_back(Current); + nextToken(); + } + Def.Body.push_back(Current); + } + + void nextToken() { + if (Pos + 1 < Tokens.size()) + ++Pos; + Current = Tokens[Pos]; + Current->Finalized = true; + } + + size_t Pos = 0; + FormatToken *Current = nullptr; + Definition Def; + ArrayRef Tokens; +}; + +MacroExpander::MacroExpander( + const std::vector &Macros, clang::SourceManager &SourceMgr, + const FormatStyle &Style, + llvm::SpecificBumpPtrAllocator &Allocator, + IdentifierTable &IdentTable) + : SourceMgr(SourceMgr), Style(Style), Allocator(Allocator), + IdentTable(IdentTable) { + for (const std::string &Macro : Macros) { + parseDefinition(Macro); + } +} + +MacroExpander::~MacroExpander() = default; + +void MacroExpander::parseDefinition(const std::string &Macro) { + Buffers.push_back( + llvm::MemoryBuffer::getMemBufferCopy(Macro, "")); + clang::FileID FID = + SourceMgr.createFileID(SourceManager::Unowned, Buffers.back().get()); + FormatTokenLexer Lex(SourceMgr, FID, 0, Style, encoding::Encoding_UTF8, + Allocator, IdentTable); + const auto Tokens = Lex.lex(); + if (!Tokens.empty()) { + DefinitionParser Parser(Tokens); + auto Definition = Parser.parse(); + Definitions[Definition.Name] = std::move(Definition); + } +} + +bool MacroExpander::defined(llvm::StringRef Name) const { + return Definitions.find(Name) != Definitions.end(); +} + +bool MacroExpander::objectLike(llvm::StringRef Name) const { + return Definitions.find(Name)->second.ObjectLike; +} + +llvm::SmallVector MacroExpander::expand(FormatToken *ID, + ArgsList Args) const { + assert(defined(ID->TokenText)); + SmallVector Result; + const Definition &Def = Definitions.find(ID->TokenText)->second; + + // Expand each argument at most once. + llvm::StringSet<> ExpandedArgs; + + // Adds the given token to Result. + auto pushToken = [&](FormatToken *Tok) { + Tok->MacroCtx->ExpandedFrom.push_back(ID); + Result.push_back(Tok); + }; + + // If Tok references a parameter, adds the corresponding argument to Result. + // Returns false if Tok does not reference a parameter. + auto expandArgument = [&](FormatToken *Tok) -> bool { + // If the current token references a parameter, expand the corresponding + // argument. + if (!Tok->is(tok::identifier) || ExpandedArgs.contains(Tok->TokenText)) + return false; + ExpandedArgs.insert(Tok->TokenText); + auto I = Def.ArgMap.find(Tok->TokenText); + if (I == Def.ArgMap.end()) + return false; + // If there are fewer arguments than referenced parameters, treat the + // parameter as empty. + // FIXME: Potentially fully abort the expansion instead. + if (I->getValue() >= Args.size()) + return true; + for (FormatToken *Arg : Args[I->getValue()]) { + // A token can be part of a macro argument at multiple levels. + // For example, with "ID(x) x": + // in ID(ID(x)), 'x' is expanded first as argument to the inner + // ID, then again as argument to the outer ID. We keep the macro + // role the token had from the inner expansion. + if (!Arg->MacroCtx) + Arg->MacroCtx = MacroExpansion(MR_ExpandedArg); + pushToken(Arg); + } + return true; + }; + + // Expand the definition into Result. + for (FormatToken *Tok : Def.Body) { + if (expandArgument(Tok)) + continue; + // Create a copy of the tokens from the macro body, i.e. were not provided + // by user code. + FormatToken *New = new (Allocator.Allocate()) FormatToken; + New->copyFrom(*Tok); + assert(!New->MacroCtx); + // Tokens that are not part of the user code are not formatted. + New->MacroCtx = MacroExpansion(MR_Hidden); + pushToken(New); + } + assert(Result.size() >= 1 && Result.back()->is(tok::eof)); + if (Result.size() > 1) { + ++Result[0]->MacroCtx->StartOfExpansion; + ++Result[Result.size() - 2]->MacroCtx->EndOfExpansion; + } + return Result; +} + +} // namespace format +} // namespace clang diff --git a/clang/lib/Format/Macros.h b/clang/lib/Format/Macros.h new file mode 100644 index 00000000000000..591ef8b5be3c7d --- /dev/null +++ b/clang/lib/Format/Macros.h @@ -0,0 +1,141 @@ +//===--- MacroExpander.h - Format C++ code ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains the main building blocks of macro support in +/// clang-format. +/// +/// In order to not violate the requirement that clang-format can format files +/// in isolation, clang-format's macro support uses expansions users provide +/// as part of clang-format's style configuration. +/// +/// Macro definitions are of the form "MACRO(p1, p2)=p1 + p2", but only support +/// one level of expansion (\see MacroExpander for a full description of what +/// is supported). +/// +/// As part of parsing, clang-format uses the MacroExpander to expand the +/// spelled token streams into expanded token streams when it encounters a +/// macro call. The UnwrappedLineParser continues to parse UnwrappedLines +/// from the expanded token stream. +/// After the expanded unwrapped lines are parsed, the MacroUnexpander matches +/// the spelled token stream into unwrapped lines that best resemble the +/// structure of the expanded unwrapped lines. +/// +/// When formatting, clang-format formats the expanded unwrapped lines first, +/// determining the token types. Next, it formats the spelled unwrapped lines, +/// keeping the token types fixed, while allowing other formatting decisions +/// to change. +/// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_FORMAT_MACROS_H +#define CLANG_LIB_FORMAT_MACROS_H + +#include +#include +#include + +#include "Encoding.h" +#include "FormatToken.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { +class MemoryBuffer; +} // namespace llvm + +namespace clang { +class IdentifierTable; +class SourceManager; + +namespace format { +struct FormatStyle; + +/// Takes a set of macro definitions as strings and allows expanding calls to +/// those macros. +/// +/// For example: +/// Definition: A(x, y)=x + y +/// Call : A(int a = 1, 2) +/// Expansion : int a = 1 + 2 +/// +/// Expansion does not check arity of the definition. +/// If fewer arguments than expected are provided, the remaining parameters +/// are considered empty: +/// Call : A(a) +/// Expansion: a + +/// If more arguments than expected are provided, they will be discarded. +/// +/// The expander does not support: +/// - recursive expansion +/// - stringification +/// - concatenation +/// - variadic macros +/// +/// Furthermore, only a single expansion of each macro argument is supported, +/// so that we cannot get conflicting formatting decisions from different +/// expansions. +/// Definition: A(x)=x+x +/// Call : A(id) +/// Expansion : id+x +/// +class MacroExpander { +public: + using ArgsList = llvm::ArrayRef>; + + /// Construct a macro expander from a set of macro definitions. + /// Macro definitions must be encoded as UTF-8. + /// + /// Each entry in \p Macros must conform to the following simple + /// macro-definition language: + /// ::= | "(" ")" + /// ::= | "" + /// ::= | "," + /// ::= "=" | + /// ::= | + /// + /// Macros that cannot be parsed will be silently discarded. + /// + MacroExpander(const std::vector &Macros, + clang::SourceManager &SourceMgr, const FormatStyle &Style, + llvm::SpecificBumpPtrAllocator &Allocator, + IdentifierTable &IdentTable); + ~MacroExpander(); + + /// Returns whether a macro \p Name is defined. + bool defined(llvm::StringRef Name) const; + + /// Returns whether the macro has no arguments and should not consume + /// subsequent parentheses. + bool objectLike(llvm::StringRef Name) const; + + /// Returns the expanded stream of format tokens for \p ID, where + /// each element in \p Args is a positional argument to the macro call. + llvm::SmallVector expand(FormatToken *ID, + ArgsList Args) const; + +private: + struct Definition; + class DefinitionParser; + + void parseDefinition(const std::string &Macro); + + clang::SourceManager &SourceMgr; + const FormatStyle &Style; + llvm::SpecificBumpPtrAllocator &Allocator; + IdentifierTable &IdentTable; + std::vector> Buffers; + llvm::StringMap Definitions; +}; + +} // namespace format +} // namespace clang + +#endif diff --git a/clang/unittests/Format/CMakeLists.txt b/clang/unittests/Format/CMakeLists.txt index d02734a48b7eb3..d0cc2cae179f17 100644 --- a/clang/unittests/Format/CMakeLists.txt +++ b/clang/unittests/Format/CMakeLists.txt @@ -15,6 +15,7 @@ add_clang_unittest(FormatTests FormatTestSelective.cpp FormatTestTableGen.cpp FormatTestTextProto.cpp + MacroExpanderTest.cpp NamespaceEndCommentsFixerTest.cpp SortImportsTestJS.cpp SortImportsTestJava.cpp diff --git a/clang/unittests/Format/MacroExpanderTest.cpp b/clang/unittests/Format/MacroExpanderTest.cpp new file mode 100644 index 00000000000000..59c67f29bedde2 --- /dev/null +++ b/clang/unittests/Format/MacroExpanderTest.cpp @@ -0,0 +1,187 @@ +#include "../../lib/Format/Macros.h" +#include "TestLexer.h" +#include "clang/Basic/FileManager.h" + +#include "gtest/gtest.h" + +namespace clang { +namespace format { + +namespace { + +class MacroExpanderTest : public ::testing::Test { +public: + std::unique_ptr + create(const std::vector &MacroDefinitions) { + return std::make_unique(MacroDefinitions, + Lex.SourceMgr.get(), Lex.Style, + Lex.Allocator, Lex.IdentTable); + } + + std::string expand(MacroExpander &Macros, llvm::StringRef Name, + const std::vector &Args = {}) { + EXPECT_TRUE(Macros.defined(Name)); + return text(Macros.expand(Lex.id(Name), lexArgs(Args))); + } + + llvm::SmallVector + lexArgs(const std::vector &Args) { + llvm::SmallVector Result; + for (const auto &Arg : Args) { + Result.push_back(uneof(Lex.lex(Arg))); + } + return Result; + } + + struct MacroAttributes { + clang::tok::TokenKind Kind; + MacroRole Role; + unsigned Start; + unsigned End; + llvm::SmallVector ExpandedFrom; + }; + + void expectAttributes(const TokenList &Tokens, + const std::vector &Attributes, + const std::string &File, unsigned Line) { + EXPECT_EQ(Tokens.size(), Attributes.size()) << text(Tokens); + for (size_t I = 0, E = Tokens.size(); I != E; ++I) { + if (I >= Attributes.size()) + continue; + std::string Context = + ("for token " + llvm::Twine(I) + ": " + Tokens[I]->Tok.getName() + + " / " + Tokens[I]->TokenText) + .str(); + EXPECT_TRUE(Tokens[I]->is(Attributes[I].Kind)) + << Context << " in " << text(Tokens) << " at " << File << ":" << Line; + EXPECT_EQ(Tokens[I]->MacroCtx->Role, Attributes[I].Role) + << Context << " in " << text(Tokens) << " at " << File << ":" << Line; + EXPECT_EQ(Tokens[I]->MacroCtx->StartOfExpansion, Attributes[I].Start) + << Context << " in " << text(Tokens) << " at " << File << ":" << Line; + EXPECT_EQ(Tokens[I]->MacroCtx->EndOfExpansion, Attributes[I].End) + << Context << " in " << text(Tokens) << " at " << File << ":" << Line; + EXPECT_EQ(Tokens[I]->MacroCtx->ExpandedFrom, Attributes[I].ExpandedFrom) + << Context << " in " << text(Tokens) << " at " << File << ":" << Line; + } + } + + TestLexer Lex; +}; + +#define EXPECT_ATTRIBUTES(Tokens, Attributes) \ + expectAttributes(Tokens, Attributes, __FILE__, __LINE__) + +TEST_F(MacroExpanderTest, SkipsDefinitionOnError) { + auto Macros = + create({"A(", "B(,", "C(a,", "D(a a", "E(a, a", "F(,)", "G(a;"}); + for (const auto *Name : {"A", "B", "C", "D", "E", "F", "G"}) { + EXPECT_FALSE(Macros->defined(Name)) << "for Name " << Name; + } +} + +TEST_F(MacroExpanderTest, ExpandsWithoutArguments) { + auto Macros = create({ + "A", + "B=b", + "C=c + c", + "D()", + }); + EXPECT_TRUE(Macros->objectLike("A")); + EXPECT_TRUE(Macros->objectLike("B")); + EXPECT_TRUE(Macros->objectLike("C")); + EXPECT_TRUE(!Macros->objectLike("D")); + EXPECT_EQ("", expand(*Macros, "A")); + EXPECT_EQ("b", expand(*Macros, "B")); + EXPECT_EQ("c+c", expand(*Macros, "C")); + EXPECT_EQ("", expand(*Macros, "D")); +} + +TEST_F(MacroExpanderTest, ExpandsWithArguments) { + auto Macros = create({ + "A(x)", + "B(x, y)=x + y", + }); + EXPECT_EQ("", expand(*Macros, "A", {"a"})); + EXPECT_EQ("b1+b2+b3", expand(*Macros, "B", {"b1", "b2 + b3"})); + EXPECT_EQ("x+", expand(*Macros, "B", {"x"})); +} + +TEST_F(MacroExpanderTest, AttributizesTokens) { + auto Macros = create({ + "A(x, y)={ x + y; }", + "B(x, y)=x + 3 + y", + }); + auto *A = Lex.id("A"); + auto AArgs = lexArgs({"a1 * a2", "a3 * a4"}); + auto Result = Macros->expand(A, AArgs); + EXPECT_EQ(11U, Result.size()) << text(Result) << " / " << Result; + EXPECT_EQ("{a1*a2+a3*a4;}", text(Result)); + std::vector Attributes = { + {tok::l_brace, MR_Hidden, 1, 0, {A}}, + {tok::identifier, MR_ExpandedArg, 0, 0, {A}}, + {tok::star, MR_ExpandedArg, 0, 0, {A}}, + {tok::identifier, MR_ExpandedArg, 0, 0, {A}}, + {tok::plus, MR_Hidden, 0, 0, {A}}, + {tok::identifier, MR_ExpandedArg, 0, 0, {A}}, + {tok::star, MR_ExpandedArg, 0, 0, {A}}, + {tok::identifier, MR_ExpandedArg, 0, 0, {A}}, + {tok::semi, MR_Hidden, 0, 0, {A}}, + {tok::r_brace, MR_Hidden, 0, 1, {A}}, + {tok::eof, MR_Hidden, 0, 0, {A}}, + }; + EXPECT_ATTRIBUTES(Result, Attributes); + + auto *B = Lex.id("B"); + auto BArgs = lexArgs({"b1", "b2"}); + Result = Macros->expand(B, BArgs); + EXPECT_EQ(6U, Result.size()) << text(Result) << " / " << Result; + EXPECT_EQ("b1+3+b2", text(Result)); + Attributes = { + {tok::identifier, MR_ExpandedArg, 1, 0, {B}}, + {tok::plus, MR_Hidden, 0, 0, {B}}, + {tok::numeric_constant, MR_Hidden, 0, 0, {B}}, + {tok::plus, MR_Hidden, 0, 0, {B}}, + {tok::identifier, MR_ExpandedArg, 0, 1, {B}}, + {tok::eof, MR_Hidden, 0, 0, {B}}, + }; + EXPECT_ATTRIBUTES(Result, Attributes); +} + +TEST_F(MacroExpanderTest, RecursiveExpansion) { + auto Macros = create({ + "A(x)=x", + "B(x)=x", + "C(x)=x", + }); + + auto *A = Lex.id("A"); + auto *B = Lex.id("B"); + auto *C = Lex.id("C"); + + auto Args = lexArgs({"id"}); + auto CResult = uneof(Macros->expand(C, Args)); + auto BResult = uneof(Macros->expand(B, CResult)); + auto AResult = uneof(Macros->expand(A, BResult)); + + std::vector Attributes = { + {tok::identifier, MR_ExpandedArg, 3, 3, {C, B, A}}, + }; + EXPECT_ATTRIBUTES(AResult, Attributes); +} + +TEST_F(MacroExpanderTest, SingleExpansion) { + auto Macros = create({"A(x)=x+x"}); + auto *A = Lex.id("A"); + auto Args = lexArgs({"id"}); + auto Result = uneof(Macros->expand(A, Args)); + std::vector Attributes = { + {tok::identifier, MR_ExpandedArg, 1, 0, {A}}, + {tok::plus, MR_Hidden, 0, 0, {A}}, + {tok::identifier, MR_Hidden, 0, 1, {A}}, + }; + EXPECT_ATTRIBUTES(Result, Attributes); +} + +} // namespace +} // namespace format +} // namespace clang diff --git a/clang/unittests/Format/TestLexer.h b/clang/unittests/Format/TestLexer.h new file mode 100644 index 00000000000000..8c5eb2b029fb37 --- /dev/null +++ b/clang/unittests/Format/TestLexer.h @@ -0,0 +1,88 @@ +//===--- TestLexer.h - Format C++ code --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file contains a TestLexer to create FormatTokens from strings. +/// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_UNITTESTS_FORMAT_TESTLEXER_H +#define CLANG_UNITTESTS_FORMAT_TESTLEXER_H + +#include "../../lib/Format/FormatTokenLexer.h" + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" + +#include +#include + +namespace clang { +namespace format { + +typedef llvm::SmallVector TokenList; + +inline std::ostream &operator<<(std::ostream &Stream, const FormatToken &Tok) { + Stream << "(" << Tok.Tok.getName() << ", \"" << Tok.TokenText.str() << "\")"; + return Stream; +} +inline std::ostream &operator<<(std::ostream &Stream, const TokenList &Tokens) { + Stream << "{"; + for (size_t I = 0, E = Tokens.size(); I != E; ++I) { + Stream << (I > 0 ? ", " : "") << *Tokens[I]; + } + Stream << "}"; + return Stream; +} + +inline TokenList uneof(const TokenList &Tokens) { + assert(!Tokens.empty() && Tokens.back()->is(tok::eof)); + return TokenList(Tokens.begin(), std::prev(Tokens.end())); +} + +inline std::string text(llvm::ArrayRef Tokens) { + return std::accumulate(Tokens.begin(), Tokens.end(), std::string(), + [](const std::string &R, FormatToken *Tok) { + return (R + Tok->TokenText).str(); + }); +} + +class TestLexer { +public: + TestLexer() : SourceMgr("test.cpp", "") {} + + TokenList lex(llvm::StringRef Code) { + Buffers.push_back( + llvm::MemoryBuffer::getMemBufferCopy(Code, "")); + clang::FileID FID = SourceMgr.get().createFileID(SourceManager::Unowned, + Buffers.back().get()); + FormatTokenLexer Lex(SourceMgr.get(), FID, 0, Style, Encoding, Allocator, + IdentTable); + auto Result = Lex.lex(); + return TokenList(Result.begin(), Result.end()); + } + + FormatToken *id(llvm::StringRef Code) { + auto Result = uneof(lex(Code)); + assert(Result.size() == 1U && "Code must expand to 1 token."); + return Result[0]; + } + + FormatStyle Style = getLLVMStyle(); + encoding::Encoding Encoding = encoding::Encoding_UTF8; + std::vector> Buffers; + clang::SourceManagerForFile SourceMgr; + llvm::SpecificBumpPtrAllocator Allocator; + IdentifierTable IdentTable; +}; + +} // namespace format +} // namespace clang + +#endif // LLVM_CLANG_UNITTESTS_FORMAT_TEST_LEXER_H From 6a1bca8798c6ba119f188061472b60876495b9ae Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Fri, 25 Sep 2020 14:08:45 +0200 Subject: [PATCH 3/8] [Analyzer] Fix unused variable warning in Release builds clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp:377:19: warning: unused variable 'Init' --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 953a8ef58b4471..cab65687444b76 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -379,8 +379,7 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction( assert(Init->isAnyMemberInitializer() && "Base and delegating initializers should have been handled by" "computeObjectUnderConstruction()"); - return addObjectUnderConstruction(State, ICC->getCXXCtorInitializer(), - LCtx, V); + return addObjectUnderConstruction(State, Init, LCtx, V); } case ConstructionContext::NewAllocatedObjectKind: { return State; From 9112567bbd1f479599e389ef9f45f820a1eab59c Mon Sep 17 00:00:00 2001 From: LLVM GN Syncbot Date: Fri, 25 Sep 2020 12:13:19 +0000 Subject: [PATCH 4/8] [gn build] Port e336b74c995 --- llvm/utils/gn/secondary/clang/lib/Format/BUILD.gn | 1 + llvm/utils/gn/secondary/clang/unittests/Format/BUILD.gn | 1 + 2 files changed, 2 insertions(+) diff --git a/llvm/utils/gn/secondary/clang/lib/Format/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/Format/BUILD.gn index e08c92312df01f..d79fffce51d1d5 100644 --- a/llvm/utils/gn/secondary/clang/lib/Format/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/Format/BUILD.gn @@ -15,6 +15,7 @@ static_library("Format") { "Format.cpp", "FormatToken.cpp", "FormatTokenLexer.cpp", + "MacroExpander.cpp", "NamespaceEndCommentsFixer.cpp", "SortJavaScriptImports.cpp", "TokenAnalyzer.cpp", diff --git a/llvm/utils/gn/secondary/clang/unittests/Format/BUILD.gn b/llvm/utils/gn/secondary/clang/unittests/Format/BUILD.gn index 5125580aec7e4b..6a7ea2487a9203 100644 --- a/llvm/utils/gn/secondary/clang/unittests/Format/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/unittests/Format/BUILD.gn @@ -23,6 +23,7 @@ unittest("FormatTests") { "FormatTestSelective.cpp", "FormatTestTableGen.cpp", "FormatTestTextProto.cpp", + "MacroExpanderTest.cpp", "NamespaceEndCommentsFixerTest.cpp", "SortImportsTestJS.cpp", "SortImportsTestJava.cpp", From 1fa06162c1cf648a6d4fac837e02b709a205f4df Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Fri, 25 Sep 2020 13:53:58 +0100 Subject: [PATCH 5/8] [SCEV] Add more tests using info from loop guards for BTC. --- .../max-backedge-taken-count-guard-info.ll | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll b/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll index 7dc309477ba4f5..ccc27643f1c93a 100644 --- a/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll +++ b/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll @@ -25,6 +25,28 @@ exit: ret void } +define void @test_guard_less_than_16_operands_swapped(i32* nocapture %a, i64 %i) { +; CHECK-LABEL: Determining loop execution counts for: @test_guard_less_than_16_operands_swapped +; CHECK-NEXT: Loop %loop: backedge-taken count is (15 + (-1 * %i)) +; CHECK-NEXT: Loop %loop: max backedge-taken count is -1 +; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is (15 + (-1 * %i)) +; +entry: + %cmp3 = icmp ugt i64 16, %i + br i1 %cmp3, label %loop, label %exit + +loop: + %iv = phi i64 [ %iv.next, %loop ], [ %i, %entry ] + %idx = getelementptr inbounds i32, i32* %a, i64 %iv + store i32 1, i32* %idx, align 4 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond = icmp eq i64 %iv.next, 16 + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + define void @test_guard_less_than_16_branches_flipped(i32* nocapture %a, i64 %i) { ; CHECK-LABEL: Determining loop execution counts for: @test_guard_less_than_16_branches_flipped ; CHECK-NEXT: Loop %loop: backedge-taken count is (15 + (-1 * %i)) @@ -69,6 +91,28 @@ exit: ret void } +define void @test_guard_eq_12(i32* nocapture %a, i64 %N) { +; CHECK-LABEL: Determining loop execution counts for: @test_guard_eq_12 +; CHECK-NEXT: Loop %loop: backedge-taken count is %N +; CHECK-NEXT: Loop %loop: max backedge-taken count is -1 +; CHECK-NEXT: Loop %loop: Predicated backedge-taken count is %N +; +entry: + %c.1 = icmp eq i64 %N, 12 + br i1 %c.1, label %loop, label %exit + +loop: + %iv = phi i64 [ %iv.next, %loop ], [ 0, %entry ] + %idx = getelementptr inbounds i32, i32* %a, i64 %iv + store i32 1, i32* %idx, align 4 + %iv.next = add nuw nsw i64 %iv, 1 + %exitcond = icmp eq i64 %iv, %N + br i1 %exitcond, label %exit, label %loop + +exit: + ret void +} + define void @test_multiple_const_guards_order1(i32* nocapture %a, i64 %i) { ; CHECK-LABEL: @test_multiple_const_guards_order1 ; CHECK: Loop %loop: backedge-taken count is %i From 9f21d341e83842c20f0cd09bb6b97617441ef55a Mon Sep 17 00:00:00 2001 From: Louis Dionne Date: Wed, 23 Sep 2020 09:20:03 -0400 Subject: [PATCH 6/8] [libc++] Initial support for pre-commit CI with Buildkite This commit adds basic files and scripts that are used for the Buildkite pre-commit CI setup. This was tested to mostly work on a fork of llvm-project, however some adjustments will have to be made as we complete the real setup. --- libcxx/utils/ci/Dockerfile | 63 +++++++++++++ libcxx/utils/ci/buildkite-pipeline.yml | 81 ++++++++++++++++ libcxx/utils/ci/phabricator-report | 86 +++++++++++++++++ libcxx/utils/ci/run-buildbot.sh | 125 +++++++++++++++++++++++++ libcxx/utils/ci/secrets.env | 11 +++ 5 files changed, 366 insertions(+) create mode 100644 libcxx/utils/ci/Dockerfile create mode 100644 libcxx/utils/ci/buildkite-pipeline.yml create mode 100755 libcxx/utils/ci/phabricator-report create mode 100755 libcxx/utils/ci/run-buildbot.sh create mode 100644 libcxx/utils/ci/secrets.env diff --git a/libcxx/utils/ci/Dockerfile b/libcxx/utils/ci/Dockerfile new file mode 100644 index 00000000000000..a83936b47083df --- /dev/null +++ b/libcxx/utils/ci/Dockerfile @@ -0,0 +1,63 @@ +#===----------------------------------------------------------------------===## +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===## + +# +# This Dockerfile describes the base image used to run the various libc++ +# build bots. By default, the image runs the Buildkite Agent, however one +# can also just start the image with a shell to debug CI failures. +# +# To start a Buildkite Agent, run it as: +# $ docker run --env-file secrets.env -it $(docker build -q .) +# +# The environment variables in `secrets.env` must be replaced by the actual +# tokens for this to work. These should obviously never be checked in. +# +# To start a shell in the Docker image, use: +# $ docker run -it --volume "$(git rev-parse --show-toplevel):/llvm" --workdir "/llvm" $(docker build -q .) bash +# +# This will fire up the Docker container and mount the root of the monorepo +# as /llvm in the container. Be careful, the state in /llvm is shared between +# the container and the host machine. +# + +FROM ubuntu:bionic + +RUN apt-get update +RUN apt-get install -y bash curl + +# Install the most recently released LLVM +RUN apt-get install -y lsb-release wget software-properties-common +RUN bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" +RUN ln -s $(find /usr/bin -regex '.+/clang\+\+-[a-zA-Z0-9.]+') /usr/bin/clang++ +RUN ln -s $(find /usr/bin -regex '.+/clang-[a-zA-Z0-9.]+') /usr/bin/clang + +# Install a recent GCC +RUN add-apt-repository ppa:ubuntu-toolchain-r/test +RUN apt-get update && apt install -y gcc-10 g++-10 +RUN ln -s $(find /usr/bin -regex '.+/g\+\+-[a-zA-Z0-9.]+') /usr/bin/g++ +RUN ln -s $(find /usr/bin -regex '.+/gcc-[a-zA-Z0-9.]+') /usr/bin/gcc + +# Install a recent CMake +RUN wget https://github.com/Kitware/CMake/releases/download/v3.18.2/cmake-3.18.2-Linux-x86_64.sh -O /tmp/install-cmake.sh +RUN bash /tmp/install-cmake.sh --prefix=/usr --exclude-subdir --skip-license +RUN rm /tmp/install-cmake.sh + +# Install other tools used by the build or the test suite +RUN apt-get install -y ninja-build python3 sphinx-doc + +# Install the Buildkite agent and dependencies +RUN bash -c "$(curl -sL https://raw.githubusercontent.com/buildkite/agent/master/install.sh)" +RUN apt-get install -y git +ENV PATH="${PATH}:/root/.buildkite-agent/bin" + +# Install the Phabricator Python module to allow uploading results to Phabricator +RUN apt-get install -y python3-pip +RUN pip3 install phabricator + +# By default, start the Buildkite agent (this requires a token). +CMD buildkite-agent start --tags "queue=libcxx-builders" diff --git a/libcxx/utils/ci/buildkite-pipeline.yml b/libcxx/utils/ci/buildkite-pipeline.yml new file mode 100644 index 00000000000000..605f103a3d9570 --- /dev/null +++ b/libcxx/utils/ci/buildkite-pipeline.yml @@ -0,0 +1,81 @@ +#===----------------------------------------------------------------------===## +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===## + +# +# This file describes the various pre-commit CI bots used to test libc++. +# +# This file should never contain logic -- all the logic must be offloaded +# into scripts. This is critical to being able to reproduce CI issues outside +# of the CI environment, which is important for debugging. +# + +steps: + - label: "C++03" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-cxx03 | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "C++11" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-cxx11 | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "C++14" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-cxx14 | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "C++17" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-cxx17 | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "C++20" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-cxx2a | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "-fno-exceptions" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-noexceptions | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "32 bits" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-32bit | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "GCC/C++20" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-gcc | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "ASAN" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-asan | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "TSAN" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-tsan | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "UBSAN" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-ubsan | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "With LLVM's libunwind" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-with_llvm_unwinder | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" + + - label: "Single-threaded" + command: "set -o pipefail && libcxx/utils/ci/run-buildbot.sh x86_64-ubuntu-singlethreaded | libcxx/utils/ci/phabricator-report" + agents: + queue: "libcxx-builders" diff --git a/libcxx/utils/ci/phabricator-report b/libcxx/utils/ci/phabricator-report new file mode 100755 index 00000000000000..14eac69c4c8042 --- /dev/null +++ b/libcxx/utils/ci/phabricator-report @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +#===----------------------------------------------------------------------===## +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===## + +import argparse +import io +import os +import phabricator +import re +import subprocess +import sys +import time + +LLVM_REVIEWS_API = "https://reviews.llvm.org/api/" + +def main(argv): + parser = argparse.ArgumentParser( + description=""" +This script gathers information about a Buildkite build and updates the +Phabricator review associated to the HEAD commit with those results. + +The intended usage of this script is to pipe the output of a command defined +in a Buildkite pipeline into it. The script will echo everything to stdout, +like tee, but will also update the Phabricator review associated to HEAD +with the results of the build. + +The script is assumed to be running inside a Buildkite agent, and as such, +it assumes the existence of several environment variables that are specific +to Buildkite. It also assumes that it is running in a context where the HEAD +commit contains the Phabricator ID of the review to update. +""") + parser.add_argument('--token', type=str, required=True, + help="The Conduit token to use to authenticate with {}.".format(LLVM_REVIEWS_API)) + args = parser.parse_args(argv) + + for var in ('BUILDKITE_LABEL', 'BUILDKITE_JOB_ID', 'BUILDKITE_BUILD_URL', 'CONDUIT_TOKEN'): + if var not in os.environ: + raise RuntimeError( + 'The {} environment variable must exist -- are you running ' + 'this script from a Buildkite agent?'.format(var)) + + # First, read all the log input and write it line-by-line to stdout. + # This is important so that we can follow progress in the Buildkite + # console. Since we're being piped into in real time, it's also the + # moment to time the duration of the job. + start = time.time() + log = io.StringIO() + while True: + line = sys.stdin.readline() + if line == '': + break + sys.stdout.write(line) + sys.stdout.flush() # flush every line to avoid buffering + log.write(line) + end = time.time() + + # Then, extract information from the environment and post-process the logs. + log.seek(0) + log = log.read() + result = 'fail' if 'FAILED:' in log else 'pass' + resultObject = { + 'name': '{BUILDKITE_LABEL} ({BUILDKITE_BUILD_URL}#{BUILDKITE_JOB_ID})'.format(**os.environ), + 'result': result, + 'duration': end - start, + 'details': log + } + + commitMessage = subprocess.check_output(['git', 'log', '--format=%B' , '-n', '1']).decode() + phabricatorID = re.search(r'^Phabricator-ID:\s+(.+)$', commitMessage, flags=re.MULTILINE) + if not phabricatorID: + raise RuntimeError('Could not find the Phabricator ID in the commit message. ' + 'The commit message was:\n{}'.format(commitMessage)) + else: + phabricatorID = phabricatorID.group(1) + + token = os.environ['CONDUIT_TOKEN'] + phab = phabricator.Phabricator(token=token, host=LLVM_REVIEWS_API) + phab.harbormaster.sendmessage(buildTargetPHID=phabricatorID, type=result, unit=[resultObject]) + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/libcxx/utils/ci/run-buildbot.sh b/libcxx/utils/ci/run-buildbot.sh new file mode 100755 index 00000000000000..4dd1d485d4c576 --- /dev/null +++ b/libcxx/utils/ci/run-buildbot.sh @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +#===----------------------------------------------------------------------===## +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===## + +set -ex + +BUILDER="${1}" + +args=() +args+=("-DLLVM_ENABLE_PROJECTS=libcxx;libunwind;libcxxabi") +args+=("-DLIBCXX_CXX_ABI=libcxxabi") + +case "${BUILDER}" in +x86_64-ubuntu-cxx03) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported --param=std=c++03") +;; +x86_64-ubuntu-cxx11) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported --param=std=c++11") +;; +x86_64-ubuntu-cxx14) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported --param=std=c++14") +;; +x86_64-ubuntu-cxx17) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported --param=std=c++17") +;; +x86_64-ubuntu-cxx2a) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported --param=std=c++2a") +;; +x86_64-ubuntu-noexceptions) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") + args+=("-DLIBCXX_ENABLE_EXCEPTIONS=OFF") + args+=("-DLIBCXXABI_ENABLE_EXCEPTIONS=OFF") +;; +x86_64-ubuntu-32bit) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") + args+=("-DLLVM_BUILD_32_BITS=ON") +;; +x86_64-ubuntu-gcc) + export CC=gcc + export CXX=g++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") +;; +x86_64-ubuntu-asan) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_USE_SANITIZER=Address") + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") +;; +x86_64-ubuntu-msan) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_USE_SANITIZER=MemoryWithOrigins") + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") +;; +x86_64-ubuntu-tsan) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_USE_SANITIZER=Thread") + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") +;; +x86_64-ubuntu-ubsan) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_USE_SANITIZER=Undefined") + args+=("-DLIBCXX_ABI_UNSTABLE=ON") + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") +;; +x86_64-ubuntu-with_llvm_unwinder) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") + args+=("-DLIBCXXABI_USE_LLVM_UNWINDER=ON") +;; +x86_64-ubuntu-singlethreaded) + export CC=clang + export CXX=clang++ + args+=("-DLLVM_LIT_ARGS=-sv --show-unsupported") + args+=("-DLIBCXX_ENABLE_THREADS=OFF") + args+=("-DLIBCXXABI_ENABLE_THREADS=OFF") + args+=("-DLIBCXX_ENABLE_MONOTONIC_CLOCK=OFF") +;; +*) + echo "${BUILDER} is not a known configuration" + exit 1 +;; +esac + +UMBRELLA_ROOT="$(git rev-parse --show-toplevel)" +LLVM_ROOT="${UMBRELLA_ROOT}/llvm" +BUILD_DIR="${UMBRELLA_ROOT}/build/${BUILDER}" + +echo "--- Generating CMake" +rm -rf "${BUILD_DIR}" +cmake -S "${LLVM_ROOT}" -B "${BUILD_DIR}" -GNinja -DCMAKE_BUILD_TYPE=RelWithDebInfo "${args[@]}" + +echo "--- Building libc++ and libc++abi" +ninja -C "${BUILD_DIR}" check-cxx-deps cxxabi + +echo "+++ Running the libc++ tests" +ninja -C "${BUILD_DIR}" check-cxx + +echo "+++ Running the libc++abi tests" +ninja -C "${BUILD_DIR}" check-cxxabi + +echo "+++ Running the libc++ benchmarks" +ninja -C "${BUILD_DIR}" check-cxx-benchmarks diff --git a/libcxx/utils/ci/secrets.env b/libcxx/utils/ci/secrets.env new file mode 100644 index 00000000000000..782bdd94a5e9f5 --- /dev/null +++ b/libcxx/utils/ci/secrets.env @@ -0,0 +1,11 @@ +# +# This file template shows which environment variables are required to run +# libc++ CI nodes. The actual values of these tokens must obviously never be +# checked in. +# + +# Required to talk with the Phabricator API to update build results +CONDUIT_TOKEN= + +# Required to register a new agent with Buildkite +BUILDKITE_AGENT_TOKEN= From df77ce7cad081bf55042cf098b61b118dcdfc7e9 Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Thu, 24 Sep 2020 18:01:55 +0100 Subject: [PATCH 7/8] [SCEV] Extract code to collect conditions to lambda (NFC). This makes re-using the common functionality easier in follow-up patches. --- llvm/lib/Analysis/ScalarEvolution.cpp | 48 ++++++++++++++++----------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp index c4e639e54e42c8..980c75f3073b48 100644 --- a/llvm/lib/Analysis/ScalarEvolution.cpp +++ b/llvm/lib/Analysis/ScalarEvolution.cpp @@ -12591,6 +12591,32 @@ const SCEV* ScalarEvolution::computeMaxBackedgeTakenCount(const Loop *L) { } const SCEV *ScalarEvolution::applyLoopGuards(const SCEV *Expr, const Loop *L) { + auto CollectCondition = [&](ICmpInst::Predicate Predicate, const SCEV *LHS, + const SCEV *RHS, ValueToSCEVMapTy &RewriteMap) { + // For now, limit to conditions that provide information about unknown + // expressions. + auto *LHSUnknown = dyn_cast(LHS); + if (!LHSUnknown) + return; + + // TODO: use information from more predicates. + switch (Predicate) { + case CmpInst::ICMP_ULT: { + if (!containsAddRecurrence(RHS)) { + const SCEV *Base = LHS; + auto I = RewriteMap.find(LHSUnknown->getValue()); + if (I != RewriteMap.end()) + Base = I->second; + + RewriteMap[LHSUnknown->getValue()] = + getUMinExpr(Base, getMinusSCEV(RHS, getOne(RHS->getType()))); + } + break; + } + default: + break; + } + }; // Starting at the loop predecessor, climb up the predecessor chain, as long // as there are predecessors that can be found that have unique successors // leading to the original header. @@ -12613,26 +12639,8 @@ const SCEV *ScalarEvolution::applyLoopGuards(const SCEV *Expr, const Loop *L) { auto Predicate = Cmp->getPredicate(); if (LoopEntryPredicate->getSuccessor(1) == Pair.second) Predicate = CmpInst::getInversePredicate(Predicate); - // TODO: use information from more predicates. - switch (Predicate) { - case CmpInst::ICMP_ULT: { - const SCEV *LHS = getSCEV(Cmp->getOperand(0)); - const SCEV *RHS = getSCEV(Cmp->getOperand(1)); - if (isa(LHS) && !isa(Cmp->getOperand(0)) && - !containsAddRecurrence(RHS)) { - const SCEV *Base = LHS; - auto I = RewriteMap.find(Cmp->getOperand(0)); - if (I != RewriteMap.end()) - Base = I->second; - - RewriteMap[Cmp->getOperand(0)] = - getUMinExpr(Base, getMinusSCEV(RHS, getOne(RHS->getType()))); - } - break; - } - default: - break; - } + CollectCondition(Predicate, getSCEV(Cmp->getOperand(0)), + getSCEV(Cmp->getOperand(1)), RewriteMap); } if (RewriteMap.empty()) From 85cea77ecb7f2ca51198ec1ad1d28845e803ee32 Mon Sep 17 00:00:00 2001 From: Aaron Ballman Date: Fri, 25 Sep 2020 10:25:17 -0400 Subject: [PATCH 8/8] Typo fix; NFC --- clang/include/clang/Basic/AttrDocs.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index a68d37ace08908..d6c3ce50f1b132 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -379,7 +379,7 @@ that appears to be capable of returning to its caller. def NoMergeDocs : Documentation { let Category = DocCatFunction; let Content = [{ -If a statement is marked ``nomerge`` and contains call experessions, those call +If a statement is marked ``nomerge`` and contains call expressions, those call expressions inside the statement will not be merged during optimization. This attribute can be used to prevent the optimizer from obscuring the source location of certain calls. For example, it will prevent tail merging otherwise