https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/220625
>From 79fa072fba69d64a1192382ce8e4dccad310838a Mon Sep 17 00:00:00 2001 From: Sirui Mu <[email protected]> Date: Wed, 2 Sep 2026 22:32:55 +0800 Subject: [PATCH] [CIR] Initial support for sanitizer attributes Introduce the `cir.sanitize` attribute for modules and functions, which gives a list of sanitizers enabled for the attached modules and functions. Added support for address sanitizer as the only supported sanitizer kind yet. Specifically, this patch contains the following changes: - It introduces a new attribute `cir.sanitize` which is essentially a list of non-duplicating `SanitizeKind`. This attribute could be attached either to the module op or to a function op to indicate the set of sanitizers enabled for the module or the function. For now, only address sanitizer is defined as a sanitizer kind; other sanitizers will be added in future patches. - It updates CIRGen to start emitting the `cir.sanitize` attribute in CIRGenModule and CIRGenFunction. - It updates LowerToLLVM pass to lower the `cir.sanitize` attribute on a function to corresponding LLVM function attributes, such as `sanitize_address`. Assisted-by: Codex / gpt-5.6-sol medium --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 29 ++++++++++++++++ .../clang/CIR/Dialect/IR/CIRDialect.td | 1 + clang/include/clang/CIR/Dialect/IR/CIROps.td | 3 +- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 19 ++++++++++- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 9 +++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 34 +++++++++++++++++++ .../CIR/Dialect/Transforms/CXXABILowering.cpp | 2 +- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 30 ++++++++++++---- clang/test/CIR/CodeGen/sanitize.c | 27 +++++++++++++++ clang/test/CIR/IR/sanitize.cir | 4 +++ 10 files changed, 148 insertions(+), 10 deletions(-) create mode 100644 clang/test/CIR/CodeGen/sanitize.c create mode 100644 clang/test/CIR/IR/sanitize.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 126f6a37a739c..d68b1fe01106f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -2136,6 +2136,35 @@ def CIR_AnnotationArrayAttr : CIR_TypedArrayAttrBase<CIR_AnnotationAttr, "array of cir.annotation attributes">; +//===----------------------------------------------------------------------===// +// SanitizeAttr +//===----------------------------------------------------------------------===// + +def CIR_SanitizeKind : CIR_I32EnumAttr<"SanitizeKind", "sanitize kind", [ + I32EnumAttrCase<"Address", 0, "address">, +]> { + let genSpecializedAttr = 0; +} + +def CIR_SanitizeAttr : CIR_Attr<"Sanitize", "sanitize"> { + let summary = "A list of sanitizers enabled for the module or function"; + let description = [{ + The `cir.sanitize` attribute is essentially a list of `SanitizeKind` values + attached to the module or a function. It represents the list of sanitizers + that should be enabled for the attached module or function. + + Each element in the `kinds` parameter is a `SanitizeKind` value. No + duplicates are allowed in the list. + }]; + + let parameters = (ins + OptionalArrayRefParameter<"cir::SanitizeKind">:$kinds + ); + let assemblyFormat = "`<` `[` (`]` `>`) : ($kinds^ `]` `>`)?"; + + let genVerifyDecl = 1; +} + include "clang/CIR/Dialect/IR/CIROpenCLAttrs.td" include "clang/CIR/Dialect/IR/CIRCUDAAttrs.td" diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td index 135cbdcc7d7be..58718502ee77f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRDialect.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRDialect.td @@ -39,6 +39,7 @@ def CIR_Dialect : Dialect { static llvm::StringRef getSizeTypeWidthAttrName() { return "cir.size_type_width"; } static llvm::StringRef getIntTypeWidthAttrName() { return "cir.int_type_width"; } static llvm::StringRef getOptInfoAttrName() { return "cir.opt_info"; } + static llvm::StringRef getSanitizeAttrName() { return "cir.sanitize"; } static llvm::StringRef getCalleeAttrName() { return "callee"; } static llvm::StringRef getNoThrowAttrName() { return "nothrow"; } static llvm::StringRef getNoReturnAttrName() { return "noreturn"; } diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 70d34884c70a4..8e4b4557b73fe 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -4280,7 +4280,8 @@ def CIR_FuncOp : CIR_Op<"func", [ CIR_OptionalPriorityAttr:$global_ctor_priority, CIR_OptionalPriorityAttr:$global_dtor_priority, OptionalAttr<CIR_FuncInfoAttr>:$func_info, - OptionalAttr<CIR_AnnotationArrayAttr>:$annotations + OptionalAttr<CIR_AnnotationArrayAttr>:$annotations, + OptionalAttr<CIR_SanitizeAttr>:$sanitize ); let regions = (region AnyRegion:$body); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 8301627ad8123..c3d5f9b66e206 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -63,7 +63,7 @@ static bool functionMightHaveBypass(const Stmt *s) { CIRGenFunction::CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, bool suppressNewContext) : CIRGenTypeCache(cgm), cgm{cgm}, builder(builder), - curFPFeatures(cgm.getLangOpts()) { + sanOpts(cgm.getLangOpts().Sanitize), curFPFeatures(cgm.getLangOpts()) { ehStack.setCGF(this); shouldEmitLifetimeMarkers = CIRGen::shouldEmitLifetimeMarkers( cgm.getCodeGenOpts(), getContext().getLangOpts()); @@ -522,6 +522,23 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, const auto *fd = dyn_cast_or_null<FunctionDecl>(d); curFuncDecl = (d ? d->getNonClosureContext() : nullptr); + if (d) { + SanitizerMask noSanitizeMask; + for (const auto *attr : d->specific_attrs<NoSanitizeAttr>()) + noSanitizeMask |= attr->getMask(); + sanOpts.Mask &= ~noSanitizeMask; + + assert(!cir::MissingFeatures::sanitizers()); + } + + llvm::SmallVector<cir::SanitizeKind> sanitizerKinds; + if (sanOpts.has(SanitizerKind::Address)) + sanitizerKinds.push_back(cir::SanitizeKind::Address); + assert(!cir::MissingFeatures::sanitizers()); + if (!sanitizerKinds.empty()) + fn.setSanitizeAttr( + cir::SanitizeAttr::get(&getMLIRContext(), sanitizerKinds)); + // This is an artifact of the legacy handling of constrained floating-point // modes. The rounding mode and exception behavior tracked in // clang::LangOptions don't correspond directly to the representation we diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 0cb2164f4c0ef..57ea1e61196ee 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -144,6 +144,15 @@ CIRGenModule::CIRGenModule(mlir::MLIRContext &mlirContext, theModule->setAttr(cir::CIRDialect::getIntTypeWidthAttrName(), builder.getI32IntegerAttr(target.getIntWidth())); + llvm::SmallVector<cir::SanitizeKind> sanitizerKinds; + if (getLangOpts().Sanitize.has(SanitizerKind::Address)) + sanitizerKinds.push_back(cir::SanitizeKind::Address); + // TODO(CIR): Register more enabled sanitizers into the sanitize attribute + assert(!cir::MissingFeatures::sanitizers()); + if (!sanitizerKinds.empty()) + theModule->setAttr(cir::CIRDialect::getSanitizeAttrName(), + cir::SanitizeAttr::get(&mlirContext, sanitizerKinds)); + if (cgo.OptimizationLevel > 0 || cgo.OptimizeSize > 0) theModule->setAttr(cir::CIRDialect::getOptInfoAttrName(), cir::OptInfoAttr::get(&mlirContext, diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 4b1909c6f3fad..a4ee4a3d86721 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2444,6 +2444,22 @@ cir::AnnotationAttr::verify(function_ref<InFlightDiagnostic()> emitError, return success(); } +//===----------------------------------------------------------------------===// +// SanitizeAttr +//===----------------------------------------------------------------------===// + +LogicalResult +cir::SanitizeAttr::verify(function_ref<InFlightDiagnostic()> emitError, + llvm::ArrayRef<cir::SanitizeKind> kinds) { + llvm::DenseSet<cir::SanitizeKind> uniqueKinds; + for (cir::SanitizeKind kind : kinds) { + if (uniqueKinds.contains(kind)) + return emitError() << "duplicate sanitize kind " << kind; + uniqueKinds.insert(kind); + } + return success(); +} + ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { llvm::SMLoc loc = parser.getCurrentLocation(); mlir::Builder &builder = parser.getBuilder(); @@ -2457,6 +2473,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name); mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name); mlir::StringAttr funcInfoNameAttr = getFuncInfoAttrName(state.name); + mlir::StringAttr sanitizeNameAttr = getSanitizeAttrName(state.name); if (::mlir::succeeded(parser.parseOptionalKeyword(builtinNameAttr.strref()))) state.addAttribute(builtinNameAttr, parser.getBuilder().getUnitAttr()); @@ -2661,6 +2678,17 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) { state.addAttribute(CIRDialect::getSideEffectAttrName(), attr); } + if (parser.parseOptionalKeyword("sanitize").succeeded()) { + if (parser.parseLParen().failed()) + return failure(); + + cir::SanitizeAttr sanitizeAttr; + if (parser.parseAttribute(sanitizeAttr).failed() || + parser.parseRParen().failed()) + return failure(); + state.addAttribute(sanitizeNameAttr, sanitizeAttr); + } + // Parse optional annotations attribute (an ArrayAttr of AnnotationAttr). mlir::StringAttr annotationsNameAttr = getAnnotationsAttrName(state.name); mlir::ArrayAttr annotationsAttr; @@ -2852,6 +2880,12 @@ void cir::FuncOp::print(OpAsmPrinter &p) { p << ")"; } + if (cir::SanitizeAttr sanitizeAttr = getSanitizeAttr()) { + p << " sanitize("; + p.printAttribute(sanitizeAttr); + p << ")"; + } + if (mlir::ArrayAttr annotations = getAnnotationsAttr()) { p << ' '; p.printAttribute(annotations); diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp index 67aa242c6018b..465b886b2bc87 100644 --- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp @@ -55,7 +55,7 @@ bool isCXXABIAttributeLegal(const mlir::TypeConverter &tc, // can have a data member/method. if (isa<mlir::DenseArrayAttr, mlir::FloatAttr, mlir::UnitAttr, mlir::StringAttr, mlir::IntegerAttr, mlir::SymbolRefAttr, - cir::AnnotationAttr>(attr)) + cir::AnnotationAttr, cir::SanitizeAttr>(attr)) return true; // Tablegen'ed always-legal attributes: diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 233e6aaa15adb..2d98dbffc8c39 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2647,7 +2647,15 @@ static bool shouldDropFuncAttribute(cir::FuncOp func, mlir::NamedAttribute attr, attr.getName() == func.getSideEffectAttrName() || attr.getName() == CIRDialect::getNoReturnAttrName() || attr.getName() == CIRDialect::getStrictFPAttrName() || - attr.getName() == func.getAnnotationsAttrName(); + attr.getName() == func.getAnnotationsAttrName() || + attr.getName() == func.getSanitizeAttrName(); +} + +static llvm::StringRef getLLVMSanitizeAttrName(cir::SanitizeKind kind) { + switch (kind) { + case cir::SanitizeKind::Address: + return "sanitize_address"; + } } /// Lower `cir.func` attributes for an `LLVMFuncOp` or `LLVM::AliasOp`. @@ -2786,13 +2794,21 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite( if (op->hasAttr(CIRDialect::getNoReturnAttrName())) fn.setNoreturn(true); - // The LLVM dialect's LLVMFuncOp has no dedicated field for the `strictfp` - // function attribute, so route it through the `passthrough` array. The MLIR - // LLVM IR translator forwards `passthrough` entries to LLVM IR as function - // attributes. + // The LLVM dialect's LLVMFuncOp has no dedicated fields for sanitizer or + // `strictfp` function attributes, so route them through the `passthrough` + // array. The MLIR LLVM IR translator forwards `passthrough` entries to LLVM + // IR as function attributes. + SmallVector<mlir::Attribute> passthroughAttrs; + if (cir::SanitizeAttr sanitizeAttr = op.getSanitizeAttr()) { + for (cir::SanitizeKind kind : sanitizeAttr.getKinds()) + passthroughAttrs.push_back( + rewriter.getStringAttr(getLLVMSanitizeAttrName(kind))); + } if (op->hasAttr(CIRDialect::getStrictFPAttrName())) - fn.setPassthroughAttr(rewriter.getArrayAttr( - {rewriter.getStringAttr(CIRDialect::getStrictFPAttrName())})); + passthroughAttrs.push_back( + rewriter.getStringAttr(CIRDialect::getStrictFPAttrName())); + if (!passthroughAttrs.empty()) + fn.setPassthroughAttr(rewriter.getArrayAttr(passthroughAttrs)); if (std::optional<cir::InlineKind> inlineKind = op.getInlineKind()) { fn.setNoInline(*inlineKind == cir::InlineKind::NoInline); diff --git a/clang/test/CIR/CodeGen/sanitize.c b/clang/test/CIR/CodeGen/sanitize.c new file mode 100644 index 0000000000000..1c7e1a51366d7 --- /dev/null +++ b/clang/test/CIR/CodeGen/sanitize.c @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir \ +// RUN: -fsanitize=address %s -o - | FileCheck %s --check-prefix=ASAN +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir \ +// RUN: %s -o - | FileCheck %s --check-prefix=NO-ASAN +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm \ +// RUN: -fsanitize=address %s -o - | FileCheck %s --check-prefix=LLVM-ASAN + +// ASAN: cir.sanitize = #cir.sanitize<[address]> + +// NO-ASAN: module +// NO-ASAN-NOT: cir.sanitize + +void foo(void) {} +// ASAN-LABEL: cir.func{{.*}}@foo +// ASAN-SAME: sanitize(#cir.sanitize<[address]>) + +// LLVM-ASAN: define {{.*}}void @foo() #[[SANITIZE_ATTR:[0-9]+]] + +__attribute__((no_sanitize("address"))) +void no_sanitize_address(void) {} +// ASAN-LABEL: cir.func{{.*}}@no_sanitize_address +// ASAN-NOT: sanitize(#cir.sanitize + +// LLVM-ASAN: define {{.*}}void @no_sanitize_address() +// LLVM-ASAN-NOT: #[[SANITIZE_ATTR]] + +// LLVM-ASAN: attributes #[[SANITIZE_ATTR]] = {{.*}}sanitize_address diff --git a/clang/test/CIR/IR/sanitize.cir b/clang/test/CIR/IR/sanitize.cir new file mode 100644 index 0000000000000..0eae2e45baf75 --- /dev/null +++ b/clang/test/CIR/IR/sanitize.cir @@ -0,0 +1,4 @@ +// RUN: cir-opt %s -verify-diagnostics + +// expected-error @below {{duplicate sanitize kind address}} +module attributes {cir.sanitize = #cir.sanitize<[address, address]>} {} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
