https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/215424
This adds an AArch64-specific implementation of TargetCIRGenInfo and adds handlers for the functions that require AArch64-specific handling. I've implemented the wouldInliningViolateFunctionCallABI function (because that seemed easier than deciding when to report NYI), generated an NYI error for isScalarizableAsmOperand in the one case where it needs to do something other than forward the call to the based class, and added MissingFeatures asserts for setTargetAttributes (because CIR doesn't support the features it wants to add attributes for yet). Assisted-by: Cursor / various models >From 1c05a8c3de6afe8d7cac011ecf39a22149b135e6 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Mon, 10 Aug 2026 15:28:44 -0700 Subject: [PATCH] [CIR] Add AArch64TargetCIRGenInfo This adds an AArch64-specific implementation of TargetCIRGenInfo and adds handlers for the functions that require AArch64-specific handling. I've implemented the wouldInliningViolateFunctionCallABI function (because that seemed easier than deciding when to report NYI), generated an NYI error for isScalarizableAsmOperand in the one case where it needs to do something other than forward the call to the based class, and added MissingFeatures asserts for setTargetAttributes (because CIR doesn't support the features it wants to add attributes for yet). Assisted-by: Cursor / various models --- clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/CIRGenModule.cpp | 6 + clang/lib/CIR/CodeGen/CMakeLists.txt | 1 + clang/lib/CIR/CodeGen/TargetInfo.cpp | 1 + clang/lib/CIR/CodeGen/TargetInfo.h | 3 + clang/lib/CIR/CodeGen/Targets/AArch64.cpp | 119 ++++++++++++++++++ .../CIR/CodeGen/aarch64-ls64-inline-asm-nyi.c | 16 +++ .../aarch64-sme-callsite-inline-attrs.c | 91 ++++++++++++++ 8 files changed, 238 insertions(+) create mode 100644 clang/lib/CIR/CodeGen/Targets/AArch64.cpp create mode 100644 clang/test/CIR/CodeGen/aarch64-ls64-inline-asm-nyi.c create mode 100644 clang/test/CIR/CodeGen/aarch64-sme-callsite-inline-attrs.c diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 02475b70c5dcd..3961df31e9dde 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -194,6 +194,7 @@ struct MissingFeatures { static bool assignMemcpyizer() { return false; } static bool astVarDeclInterface() { return false; } static bool attributeNoBuiltin() { return false; } + static bool branchProtection() { return false; } static bool builtinCall() { return false; } static bool builtinCallF128() { return false; } static bool builtinBitCountExpr() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index ae9cad0b7c30f..b2f3fe195ac33 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -308,6 +308,12 @@ const TargetCIRGenInfo &CIRGenModule::getTargetCIRGenInfo() { return *theTargetCIRGenInfo; } } + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_32: + case llvm::Triple::aarch64_be: { + theTargetCIRGenInfo = createAArch64TargetCIRGenInfo(genTypes); + return *theTargetCIRGenInfo; + } case llvm::Triple::nvptx: case llvm::Triple::nvptx64: theTargetCIRGenInfo = createNVPTXTargetCIRGenInfo(genTypes); diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 1fe9b25dd786c..3ea96a8ea05e9 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -56,6 +56,7 @@ add_clang_library(clangCIR CIRGenTypes.cpp CIRGenVTables.cpp TargetInfo.cpp + Targets/AArch64.cpp Targets/AMDGPU.cpp Targets/NVPTX.cpp Targets/SPIRV.cpp diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp index ba7eeb29dd252..584140a20f838 100644 --- a/clang/lib/CIR/CodeGen/TargetInfo.cpp +++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp @@ -120,6 +120,7 @@ class X8664TargetCIRGenInfo : public TargetCIRGenInfo { X8664TargetCIRGenInfo(CIRGenTypes &cgt) : TargetCIRGenInfo(std::make_unique<X8664ABIInfo>(cgt)) {} }; + } // namespace std::unique_ptr<TargetCIRGenInfo> diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h index e720a4ad2ec5c..eb9e431034bc9 100644 --- a/clang/lib/CIR/CodeGen/TargetInfo.h +++ b/clang/lib/CIR/CodeGen/TargetInfo.h @@ -171,6 +171,9 @@ void setAMDGPUTargetFunctionAttributes(const clang::Decl *decl, std::unique_ptr<TargetCIRGenInfo> createX8664TargetCIRGenInfo(CIRGenTypes &cgt); +std::unique_ptr<TargetCIRGenInfo> +createAArch64TargetCIRGenInfo(CIRGenTypes &cgt); + std::unique_ptr<TargetCIRGenInfo> createNVPTXTargetCIRGenInfo(CIRGenTypes &cgt); std::unique_ptr<TargetCIRGenInfo> createSPIRVTargetCIRGenInfo(CIRGenTypes &cgt); diff --git a/clang/lib/CIR/CodeGen/Targets/AArch64.cpp b/clang/lib/CIR/CodeGen/Targets/AArch64.cpp new file mode 100644 index 0000000000000..5407beae5c696 --- /dev/null +++ b/clang/lib/CIR/CodeGen/Targets/AArch64.cpp @@ -0,0 +1,119 @@ +//===---- AArch64.cpp - AArch64-specific CIR CodeGen ----------------------===// +// +// 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 provides AArch64-specific CIR CodeGen logic. +// +//===----------------------------------------------------------------------===// + +#include "../ABIInfo.h" +#include "../CIRGenFunction.h" +#include "../CIRGenModule.h" +#include "../TargetInfo.h" +#include "clang/AST/Decl.h" +#include "clang/CIR/MissingFeatures.h" + +using namespace clang; +using namespace clang::CIRGen; + +namespace { + +class AArch64ABIInfo : public ABIInfo { +public: + AArch64ABIInfo(CIRGenTypes &cgt) : ABIInfo(cgt) {} +}; + +class AArch64TargetCIRGenInfo : public TargetCIRGenInfo { +public: + AArch64TargetCIRGenInfo(CIRGenTypes &cgt) + : TargetCIRGenInfo(std::make_unique<AArch64ABIInfo>(cgt)) {} + + void setTargetAttributes(const Decl *d, mlir::Operation *gv, + CIRGenModule &cgm) const override { + auto fn = mlir::dyn_cast<cir::FuncOp>(gv); + if (!fn) + return; + assert(!cir::MissingFeatures::branchProtection()); + assert(!cir::MissingFeatures::pointerAuthentication()); + } + + bool isScalarizableAsmOperand(CIRGenFunction &cgf, + mlir::Type ty) const override { + if (cgf.getTarget().hasFeature("ls64")) { + cgf.cgm.errorNYI("AArch64 LS64 scalarizable asm operand"); + return true; + } + return TargetCIRGenInfo::isScalarizableAsmOperand(cgf, ty); + } + + bool wouldInliningViolateFunctionCallABI( + const FunctionDecl *caller, const FunctionDecl *callee) const override; +}; + +} // namespace + +// TODO(cir): Find a way to share this with classic codegen. +enum class ArmSMEInlinability : uint8_t { + Ok = 0, + ErrorCalleeRequiresNewZA = 1 << 0, + ErrorCalleeRequiresNewZT0 = 1 << 1, + WarnIncompatibleStreamingModes = 1 << 2, + ErrorIncompatibleStreamingModes = 1 << 3, + + IncompatibleStreamingModes = + WarnIncompatibleStreamingModes | ErrorIncompatibleStreamingModes, + + LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/ErrorIncompatibleStreamingModes), +}; + +static bool isStreamingCompatible(const FunctionDecl *fd) { + if (const auto *fpt = fd->getType()->getAs<FunctionProtoType>()) + return fpt->getAArch64SMEAttributes() & + clang::FunctionType::SME_PStateSMCompatibleMask; + return false; +} + +/// Determines if there are any Arm SME ABI issues with inlining \p Callee into +/// \p Caller. Returns the issue (if any) in the ArmSMEInlinability bit enum. +static ArmSMEInlinability getArmSMEInlinability(const FunctionDecl *caller, + const FunctionDecl *callee) { + bool callerIsStreaming = + clang::IsArmStreamingFunction(caller, /*IncludeLocallyStreaming=*/true); + bool calleeIsStreaming = + clang::IsArmStreamingFunction(callee, /*IncludeLocallyStreaming=*/true); + bool callerIsStreamingCompatible = isStreamingCompatible(caller); + bool calleeIsStreamingCompatible = isStreamingCompatible(callee); + + ArmSMEInlinability inlinability = ArmSMEInlinability::Ok; + + if (!calleeIsStreamingCompatible && + (callerIsStreaming != calleeIsStreaming || callerIsStreamingCompatible)) { + if (calleeIsStreaming) + inlinability |= ArmSMEInlinability::ErrorIncompatibleStreamingModes; + else + inlinability |= ArmSMEInlinability::WarnIncompatibleStreamingModes; + } + if (auto *newAttr = callee->getAttr<ArmNewAttr>()) { + if (newAttr->isNewZA()) + inlinability |= ArmSMEInlinability::ErrorCalleeRequiresNewZA; + if (newAttr->isNewZT0()) + inlinability |= ArmSMEInlinability::ErrorCalleeRequiresNewZT0; + } + + return inlinability; +} + +bool AArch64TargetCIRGenInfo::wouldInliningViolateFunctionCallABI( + const FunctionDecl *caller, const FunctionDecl *callee) const { + return caller && callee && + getArmSMEInlinability(caller, callee) != ArmSMEInlinability::Ok; +} + +std::unique_ptr<TargetCIRGenInfo> +clang::CIRGen::createAArch64TargetCIRGenInfo(CIRGenTypes &cgt) { + return std::make_unique<AArch64TargetCIRGenInfo>(cgt); +} diff --git a/clang/test/CIR/CodeGen/aarch64-ls64-inline-asm-nyi.c b/clang/test/CIR/CodeGen/aarch64-ls64-inline-asm-nyi.c new file mode 100644 index 0000000000000..60486cf9c0c5f --- /dev/null +++ b/clang/test/CIR/CodeGen/aarch64-ls64-inline-asm-nyi.c @@ -0,0 +1,16 @@ +// RUN: not %clang_cc1 -triple aarch64 -target-feature +ls64 -fclangir \ +// RUN: -emit-cir %s -o /dev/null 2>&1 | FileCheck %s + +struct data512 { + unsigned long long data[8]; +}; + +void store(const struct data512 *input, void *addr) { + __asm__ volatile("st64b %0, [%1]" + : + : "r"(*input), "r"(addr) + : "memory"); +} + +// CHECK: error: ClangIR code gen Not Yet Implemented: AArch64 LS64 +// CHECK-SAME: scalarizable asm operand diff --git a/clang/test/CIR/CodeGen/aarch64-sme-callsite-inline-attrs.c b/clang/test/CIR/CodeGen/aarch64-sme-callsite-inline-attrs.c new file mode 100644 index 0000000000000..5f2c98bd7abca --- /dev/null +++ b/clang/test/CIR/CodeGen/aarch64-sme-callsite-inline-attrs.c @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme \ +// RUN: -target-feature +sme2 -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme \ +// RUN: -target-feature +sme2 -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme \ +// RUN: -target-feature +sme2 -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s + +volatile int value; + +__attribute__((noinline)) static void was_inlined(void) { ++value; } + +void normal(void) { was_inlined(); } +void compatible(void) __arm_streaming_compatible { was_inlined(); } +void streaming(void) __arm_streaming { was_inlined(); } +__arm_new("za") void new_za(void) { was_inlined(); } +__arm_new("zt0") void new_zt0(void) { was_inlined(); } + +// CIR-LABEL: cir.func{{.*}} @normal_caller() +// LLVM-LABEL: define{{.*}} void @normal_caller() +void normal_caller(void) { + [[clang::always_inline]] normal(); + // CIR: cir.call @normal() {inline_kind = #cir.inline_kind<always_inline>} + // LLVM-NOT: call void @normal() + // LLVM: call void @was_inlined() + + [[clang::always_inline]] compatible(); + // CIR: cir.call @compatible() {inline_kind = #cir.inline_kind<always_inline>} + // LLVM-NOT: call void @compatible() + // LLVM: call void @was_inlined() + + [[clang::always_inline]] streaming(); + // CIR: cir.call @streaming() + // CIR-NOT: inline_kind + // LLVM: call void @streaming() + + [[clang::always_inline]] new_za(); + // CIR: cir.call @new_za() + // CIR-NOT: inline_kind + // LLVM: call void @new_za() + + [[clang::always_inline]] new_zt0(); + // CIR: cir.call @new_zt0() + // CIR-NOT: inline_kind + // CIR: cir.return + // LLVM: call void @new_zt0() + // LLVM: ret void +} + +// CIR-LABEL: cir.func{{.*}} @compatible_caller() +// LLVM-LABEL: define{{.*}} void @compatible_caller() +void compatible_caller(void) __arm_streaming_compatible { + [[clang::always_inline]] normal(); + // CIR: cir.call @normal() + // CIR-NOT: inline_kind + // LLVM: call void @normal() + + [[clang::always_inline]] compatible(); + // CIR: cir.call @compatible() {inline_kind = #cir.inline_kind<always_inline>} + // LLVM-NOT: call void @compatible() + // LLVM: call void @was_inlined() + + [[clang::always_inline]] streaming(); + // CIR: cir.call @streaming() + // CIR-NOT: inline_kind + // CIR: cir.return + // LLVM: call void @streaming() + // LLVM: ret void +} + +// CIR-LABEL: cir.func{{.*}} @streaming_caller() +// LLVM-LABEL: define{{.*}} void @streaming_caller() +void streaming_caller(void) __arm_streaming { + [[clang::always_inline]] normal(); + // CIR: cir.call @normal() + // CIR-NOT: inline_kind + // LLVM: call void @normal() + + [[clang::always_inline]] compatible(); + // CIR: cir.call @compatible() {inline_kind = #cir.inline_kind<always_inline>} + // LLVM-NOT: call void @compatible() + // LLVM: call void @was_inlined() + + [[clang::always_inline]] streaming(); + // CIR: cir.call @streaming() {inline_kind = #cir.inline_kind<always_inline>} + // LLVM-NOT: call void @streaming() + // LLVM: call void @was_inlined() + // LLVM: ret void +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
