diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 26fdffe920e95..aff9ce3782532 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -416,6 +416,15 @@ Arm and AArch64 Support - Clang builtin ``__arithmetic_fence`` and the command line option ``-fprotect-parens`` are now enabled for AArch64. +- Clang supports flag output operands by which conditions in the NZCV could be outputs + of inline assembly for AArch64. This change is more consistent with the behavior of + GCC. + + .. code-block:: c + + // int a = foo(); int* b = bar(); + asm("ands %w[a], %w[a], #3" : [a] "+r"(a), "=@cceq"(*b)); + Windows Support ^^^^^^^^^^^^^^^ diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp index 67ba5f6b35145..3840139d27434 100644 --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -333,6 +333,8 @@ void AArch64TargetInfo::getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const { // Target identification. Builder.defineMacro("__aarch64__"); + // Inline assembly supports AArch64 flag outputs. + Builder.defineMacro("__GCC_ASM_FLAG_OUTPUTS__"); // For bare-metal. if (getTriple().getOS() == llvm::Triple::UnknownOS && getTriple().isOSBinFormatELF()) @@ -1210,6 +1212,52 @@ ArrayRef AArch64TargetInfo::getGCCRegAliases() const { return llvm::ArrayRef(GCCRegAliases); } +// Returns the length of cc constraint. +static unsigned matchAsmCCConstraint(const char *Name) { + constexpr unsigned len = 5; + auto RV = llvm::StringSwitch(Name) + .Case("@cceq", len) + .Case("@ccne", len) + .Case("@cchs", len) + .Case("@cccs", len) + .Case("@cccc", len) + .Case("@cclo", len) + .Case("@ccmi", len) + .Case("@ccpl", len) + .Case("@ccvs", len) + .Case("@ccvc", len) + .Case("@cchi", len) + .Case("@ccls", len) + .Case("@ccge", len) + .Case("@cclt", len) + .Case("@ccgt", len) + .Case("@ccle", len) + .Default(0); + return RV; +} + +std::string +AArch64TargetInfo::convertConstraint(const char *&Constraint) const { + std::string R; + switch (*Constraint) { + case 'U': // Three-character constraint; add "@3" hint for later parsing. + R = std::string("@3") + std::string(Constraint, 3); + Constraint += 2; + break; + case '@': + if (const unsigned Len = matchAsmCCConstraint(Constraint)) { + std::string Converted = "{" + std::string(Constraint, Len) + "}"; + Constraint += Len - 1; + return Converted; + } + return std::string(1, *Constraint); + default: + R = TargetInfo::convertConstraint(Constraint); + break; + } + return R; +} + bool AArch64TargetInfo::validateAsmConstraint( const char *&Name, TargetInfo::ConstraintInfo &Info) const { switch (*Name) { @@ -1257,6 +1305,13 @@ bool AArch64TargetInfo::validateAsmConstraint( case 'y': // SVE registers (V0-V7) Info.setAllowsRegister(); return true; + case '@': + // CC condition + if (const unsigned Len = matchAsmCCConstraint(Name)) { + Name += Len - 1; + Info.setAllowsRegister(); + return true; + } } return false; } diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index 967a888f6b6f8..cce80e10f2bb1 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -173,19 +173,7 @@ class LLVM_LIBRARY_VISIBILITY AArch64TargetInfo : public TargetInfo { ArrayRef getGCCRegNames() const override; ArrayRef getGCCRegAliases() const override; - std::string convertConstraint(const char *&Constraint) const override { - std::string R; - switch (*Constraint) { - case 'U': // Three-character constraint; add "@3" hint for later parsing. - R = std::string("@3") + std::string(Constraint, 3); - Constraint += 2; - break; - default: - R = TargetInfo::convertConstraint(Constraint); - break; - } - return R; - } + std::string convertConstraint(const char *&Constraint) const override; bool validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &Info) const override; diff --git a/clang/test/CodeGen/inline-asm-aarch64-flag-output.c b/clang/test/CodeGen/inline-asm-aarch64-flag-output.c new file mode 100644 index 0000000000000..b1ef6e3883754 --- /dev/null +++ b/clang/test/CodeGen/inline-asm-aarch64-flag-output.c @@ -0,0 +1,130 @@ +// RUN: %clang_cc1 -O2 -emit-llvm %s -o - -triple aarch64 | FileCheck %s + +int test_cceq(int a, int* b) { +// CHECK-LABEL: @test_cceq +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@cceq},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@cceq"(*b)); + return a; +} + +int test_ccne(int a, int* b) { +// CHECK-LABEL: @test_ccne +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@ccne},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@ccne"(*b)); + return a; +} + +int test_cccs(int a, int* b) { +// CHECK-LABEL: @test_cccs +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@cccs},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@cccs"(*b)); + return a; +} + +int test_cchs(int a, int* b) { +// CHECK-LABEL: @test_cchs +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@cchs},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@cchs"(*b)); + return a; +} + +int test_cccc(int a, int* b) { +// CHECK-LABEL: @test_cccc +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@cccc},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@cccc"(*b)); + return a; +} + +int test_cclo(int a, int* b) { +// CHECK-LABEL: @test_cclo +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@cclo},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@cclo"(*b)); + return a; +} + +int test_ccmi(int a, int* b) { +// CHECK-LABEL: @test_ccmi +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@ccmi},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@ccmi"(*b)); + return a; +} + +int test_ccpl(int a, int* b) { +// CHECK-LABEL: @test_ccpl +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@ccpl},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@ccpl"(*b)); + return a; +} + +int test_ccvs(int a, int* b) { +// CHECK-LABEL: @test_ccvs +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@ccvs},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@ccvs"(*b)); + return a; +} + +int test_ccvc(int a, int* b) { +// CHECK-LABEL: @test_ccvc +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@ccvc},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@ccvc"(*b)); + return a; +} + +int test_cchi(int a, int* b) { +// CHECK-LABEL: @test_cchi +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@cchi},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@cchi"(*b)); + return a; +} + +int test_ccls(int a, int* b) { +// CHECK-LABEL: @test_ccls +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@ccls},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@ccls"(*b)); + return a; +} + + +int test_ccge(int a, int* b) { +// CHECK-LABEL: @test_ccge +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@ccge},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@ccge"(*b)); + return a; +} + +int test_cclt(int a, int* b) { +// CHECK-LABEL: @test_cclt +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@cclt},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@cclt"(*b)); + return a; +} + +int test_ccgt(int a, int* b) { +// CHECK-LABEL: @test_ccgt +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@ccgt},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@ccgt"(*b)); + return a; +} + +int test_ccle(int a, int* b) { +// CHECK-LABEL: @test_ccle +// CHECK: = tail call { i32, i32 } asm "ands ${0:w}, ${0:w}, #3", "=r,={@ccle},0"(i32 %a) + asm("ands %w[a], %w[a], #3" + : [a] "+r"(a), "=@ccle"(*b)); + return a; +} diff --git a/clang/test/Preprocessor/aarch64_asm_flag_output.c b/clang/test/Preprocessor/aarch64_asm_flag_output.c new file mode 100644 index 0000000000000..28b5282132099 --- /dev/null +++ b/clang/test/Preprocessor/aarch64_asm_flag_output.c @@ -0,0 +1,3 @@ +// RUN: %clang -target aarch64-unknown-unknown -x c -E -dM -o - %s | FileCheck -match-full-lines %s + +// CHECK: #define __GCC_ASM_FLAG_OUTPUTS__ 1 diff --git a/clang/test/Preprocessor/init-aarch64.c b/clang/test/Preprocessor/init-aarch64.c index 869d7ca8a0636..4ef62f0e2aa8d 100644 --- a/clang/test/Preprocessor/init-aarch64.c +++ b/clang/test/Preprocessor/init-aarch64.c @@ -106,6 +106,7 @@ // AARCH64-NEXT: #define __FLT_RADIX__ 2 // AARCH64-NEXT: #define __FP_FAST_FMA 1 // AARCH64-NEXT: #define __FP_FAST_FMAF 1 +// AARCH64-NEXT: #define __GCC_ASM_FLAG_OUTPUTS__ 1 // AARCH64-NEXT: #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_1 1 // AARCH64-NEXT: #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_2 1 // AARCH64-NEXT: #define __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4 1 diff --git a/clang/test/Preprocessor/predefined-win-macros.c b/clang/test/Preprocessor/predefined-win-macros.c index f402ce91838d6..4497d14c6a5ef 100644 --- a/clang/test/Preprocessor/predefined-win-macros.c +++ b/clang/test/Preprocessor/predefined-win-macros.c @@ -129,4 +129,5 @@ // CHECK-ARM64-MINGW: #define WINNT 1 // CHECK-ARM64-MINGW: #define _WIN32 1 // CHECK-ARM64-MINGW: #define _WIN64 1 +// CHECK-ARM64-MINGW: #define __GCC_ASM_FLAG_OUTPUTS__ 1 // CHECK-ARM64-MINGW: #define __aarch64__ 1