[llvm] [LLVM][rtsan] Add nonblocking Attribute and RealtimeSanitizer pass (PR #100596)
Chris Apple via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 25 09:49:14 PDT 2024
https://github.com/cjappl created https://github.com/llvm/llvm-project/pull/100596
Add the RealtimeSanitizer pass to llvm. This also required adding a new "nonblocking" attribute, which will correspond to the nonblocking function effect in clang.
Please see the [reviewer support document](https://github.com/realtime-sanitizer/radsan/blob/doc/review-support/doc/review.md) for what our next steps are. The original discourse thread can be found [here](https://discourse.llvm.org/t/rfc-nolock-and-noalloc-attributes/76837)
>From 695c74e978e1b1656c8dd4a204c7fcfe2b774afd Mon Sep 17 00:00:00 2001
From: Chris Apple <cja-private at pm.me>
Date: Wed, 24 Jul 2024 07:32:15 -0700
Subject: [PATCH] [LLVM][rtsan] Add NonBlocking Attribute and RealtimeSanitizer
pass
---
llvm/CODE_OWNERS.TXT | 4 ++
llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 +
llvm/include/llvm/IR/Attributes.td | 3 ++
.../Instrumentation/RealtimeSanitizer.h | 35 +++++++++++++
.../RealtimeSanitizerOptions.h | 17 +++++++
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 2 +
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 2 +
llvm/lib/Passes/PassBuilder.cpp | 6 +++
llvm/lib/Passes/PassRegistry.def | 4 ++
.../Transforms/Instrumentation/CMakeLists.txt | 1 +
.../Instrumentation/RealtimeSanitizer.cpp | 50 +++++++++++++++++++
llvm/lib/Transforms/Utils/CodeExtractor.cpp | 1 +
llvm/test/Bitcode/attributes.ll | 7 +++
llvm/test/Bitcode/compatibility.ll | 8 ++-
.../RealtimeSanitizer/rtsan.ll | 35 +++++++++++++
15 files changed, 174 insertions(+), 2 deletions(-)
create mode 100644 llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h
create mode 100644 llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h
create mode 100644 llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
create mode 100644 llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll
diff --git a/llvm/CODE_OWNERS.TXT b/llvm/CODE_OWNERS.TXT
index d1620d1cbf870..c996cae8f97c5 100644
--- a/llvm/CODE_OWNERS.TXT
+++ b/llvm/CODE_OWNERS.TXT
@@ -263,3 +263,7 @@ D: C-SKY backend (lib/Target/CSKY/*)
N: Ilia Diachkov
E: ilia.diachkov at gmail.com
D: SPIR-V backend (lib/Target/SPIRV/*)
+
+N: Christopher Apple, David Trevelyan
+E: cja-private at pm.me, realtime.sanitizer at gmail.com
+D: RealtimeSanitizer (LLVM part)
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index fb88f2fe75adb..61b046c26a97f 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -758,6 +758,7 @@ enum AttributeKindCodes {
ATTR_KIND_SANITIZE_NUMERICAL_STABILITY = 93,
ATTR_KIND_INITIALIZES = 94,
ATTR_KIND_HYBRID_PATCHABLE = 95,
+ ATTR_KIND_NONBLOCKING = 96,
};
enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index e1bd193891c1e..8d90e912f279c 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -239,6 +239,9 @@ def ReadNone : EnumAttr<"readnone", [ParamAttr]>;
/// Function only reads from memory.
def ReadOnly : EnumAttr<"readonly", [ParamAttr]>;
+/// Function is marked to be non-blocking
+def NonBlocking : EnumAttr<"nonblocking", [FnAttr]>;
+
/// Return value is always equal to this argument.
def Returned : EnumAttr<"returned", [ParamAttr]>;
diff --git a/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h
new file mode 100644
index 0000000000000..9cf2448361608
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h
@@ -0,0 +1,35 @@
+//===--------- Definition of the RealtimeSanitizer class ---------*- C++
+//-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZER_H
+#define LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZER_H
+
+#include "llvm/IR/PassManager.h"
+#include "llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h"
+
+namespace llvm {
+
+struct RealtimeSanitizerOptions {};
+
+class RealtimeSanitizerPass : public PassInfoMixin<RealtimeSanitizerPass> {
+public:
+ RealtimeSanitizerPass(const RealtimeSanitizerOptions &Options);
+ PreservedAnalyses run(Function &F, AnalysisManager<Function> &AM);
+
+ static bool isRequired() { return true; }
+
+private:
+ RealtimeSanitizerOptions Options{};
+};
+
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZER_H
diff --git a/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h
new file mode 100644
index 0000000000000..35376a5647c60
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h
@@ -0,0 +1,17 @@
+//===--------- Definition of the RealtimeSanitizer options -------*- C++
+//-*-===//
+//
+// 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 defines data types used to set Realtime Sanitizer options.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZEROPTIONS_H
+#define LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZEROPTIONS_H
+
+namespace llvm {} // namespace llvm
+
+#endif
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 84d624f6cf8fa..a519748bc1c83 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2109,6 +2109,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::ReadOnly;
case bitc::ATTR_KIND_RETURNED:
return Attribute::Returned;
+ case bitc::ATTR_KIND_NONBLOCKING:
+ return Attribute::NonBlocking;
case bitc::ATTR_KIND_RETURNS_TWICE:
return Attribute::ReturnsTwice;
case bitc::ATTR_KIND_S_EXT:
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 324dcbca8137e..d54aeae74a59f 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -805,6 +805,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_OPTIMIZE_FOR_SIZE;
case Attribute::OptimizeNone:
return bitc::ATTR_KIND_OPTIMIZE_NONE;
+ case Attribute::NonBlocking:
+ return bitc::ATTR_KIND_NONBLOCKING;
case Attribute::ReadNone:
return bitc::ATTR_KIND_READ_NONE;
case Attribute::ReadOnly:
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 5dbb1e2f49871..faf3e25cb31d9 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -198,6 +198,7 @@
#include "llvm/Transforms/Instrumentation/PGOForceFunctionAttrs.h"
#include "llvm/Transforms/Instrumentation/PGOInstrumentation.h"
#include "llvm/Transforms/Instrumentation/PoisonChecking.h"
+#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h"
#include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"
#include "llvm/Transforms/Instrumentation/SanitizerCoverage.h"
#include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
@@ -1207,6 +1208,11 @@ parseRegAllocFastPassOptions(PassBuilder &PB, StringRef Params) {
return Opts;
}
+Expected<RealtimeSanitizerOptions> parseRtSanPassOptions(StringRef Params) {
+ RealtimeSanitizerOptions Result;
+ return Result;
+}
+
} // namespace
/// Tests whether a pass name starts with a valid prefix for a default pipeline
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 3b92823cd283b..f795c7242f958 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -590,6 +590,10 @@ FUNCTION_PASS_WITH_PARAMS(
return WinEHPreparePass(DemoteCatchSwitchPHIOnly);
},
parseWinEHPrepareOptions, "demote-catchswitch-only")
+FUNCTION_PASS_WITH_PARAMS(
+ "rtsan", "RealtimeSanitizerPass",
+ [](RealtimeSanitizerOptions Opts) { return RealtimeSanitizerPass(Opts); },
+ parseRtSanPassOptions, "")
#undef FUNCTION_PASS_WITH_PARAMS
#ifndef LOOPNEST_PASS
diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
index 4e3f9e27e0c34..deab37801ff1d 100644
--- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
+++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
@@ -25,6 +25,7 @@ add_llvm_component_library(LLVMInstrumentation
ValueProfileCollector.cpp
ThreadSanitizer.cpp
HWAddressSanitizer.cpp
+ RealtimeSanitizer.cpp
ADDITIONAL_HEADER_DIRS
${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms
diff --git a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
new file mode 100644
index 0000000000000..2fa2389c4984f
--- /dev/null
+++ b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp
@@ -0,0 +1,50 @@
+#include "llvm/IR/Analysis.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Module.h"
+
+#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h"
+
+using namespace llvm;
+
+namespace {
+
+void insertCallBeforeInstruction(Function &Fn, Instruction &Instruction,
+ const char *FunctionName) {
+ LLVMContext &Context = Fn.getContext();
+ FunctionType *FuncType = FunctionType::get(Type::getVoidTy(Context), false);
+ FunctionCallee Func =
+ Fn.getParent()->getOrInsertFunction(FunctionName, FuncType);
+ IRBuilder<> Builder{&Instruction};
+ Builder.CreateCall(Func, {});
+}
+
+void insertCallAtFunctionEntryPoint(Function &Fn, const char *InsertFnName) {
+
+ insertCallBeforeInstruction(Fn, Fn.front().front(), InsertFnName);
+}
+
+void insertCallAtAllFunctionExitPoints(Function &Fn, const char *InsertFnName) {
+ for (auto &BB : Fn) {
+ for (auto &I : BB) {
+ if (auto *RI = dyn_cast<ReturnInst>(&I)) {
+ insertCallBeforeInstruction(Fn, I, InsertFnName);
+ }
+ }
+ }
+}
+} // namespace
+
+RealtimeSanitizerPass::RealtimeSanitizerPass(
+ const RealtimeSanitizerOptions &Options)
+ : Options{Options} {}
+
+PreservedAnalyses RealtimeSanitizerPass::run(Function &F,
+ AnalysisManager<Function> &AM) {
+ if (F.hasFnAttribute(Attribute::NonBlocking)) {
+ insertCallAtFunctionEntryPoint(F, "__rtsan_realtime_enter");
+ insertCallAtAllFunctionExitPoints(F, "__rtsan_realtime_exit");
+ return PreservedAnalyses::none();
+ }
+
+ return PreservedAnalyses::all();
+}
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index 5bca5cf8ff91f..1339bbb3842d4 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -946,6 +946,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
case Attribute::NoUnwind:
case Attribute::NoSanitizeBounds:
case Attribute::NoSanitizeCoverage:
+ case Attribute::NonBlocking:
case Attribute::NullPointerIsValid:
case Attribute::OptimizeForDebugging:
case Attribute::OptForFuzzing:
diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index f4dc9b9849827..5f8617dad8dfd 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -505,6 +505,12 @@ define void @f86() nosanitize_bounds
ret void;
}
+; CHECK: define void @f92() #53
+define void @f92() nonblocking
+{
+ ret void;
+}
+
; CHECK: define void @f87() [[FNRETTHUNKEXTERN:#[0-9]+]]
define void @f87() fn_ret_thunk_extern { ret void }
@@ -599,6 +605,7 @@ define void @initializes(ptr initializes((-4, 0), (4, 8)) %a) {
; CHECK: attributes #50 = { disable_sanitizer_instrumentation }
; CHECK: attributes #51 = { uwtable(sync) }
; CHECK: attributes #52 = { nosanitize_bounds }
+; CHECK: attributes #53 = { nonblocking }
; CHECK: attributes [[FNRETTHUNKEXTERN]] = { fn_ret_thunk_extern }
; CHECK: attributes [[SKIPPROFILE]] = { skipprofile }
; CHECK: attributes [[OPTDEBUG]] = { optdebug }
diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll
index e437c37d8d1c8..1471138b91a9f 100644
--- a/llvm/test/Bitcode/compatibility.ll
+++ b/llvm/test/Bitcode/compatibility.ll
@@ -1564,7 +1564,7 @@ exit:
; CHECK: select <2 x i1> <i1 true, i1 false>, <2 x i8> <i8 2, i8 3>, <2 x i8> <i8 3, i8 2>
call void @f.nobuiltin() builtin
- ; CHECK: call void @f.nobuiltin() #52
+ ; CHECK: call void @f.nobuiltin() #53
call fastcc noalias ptr @f.noalias() noinline
; CHECK: call fastcc noalias ptr @f.noalias() #12
@@ -1991,6 +1991,9 @@ declare void @f.allockind() allockind("alloc,uninitialized")
declare void @f.sanitize_numerical_stability() sanitize_numerical_stability
; CHECK: declare void @f.sanitize_numerical_stability() #51
+declare void @f.nonblocking() nonblocking
+; CHECK: declare void @f.nonblocking() #52
+
; CHECK: declare nofpclass(snan) float @nofpclass_snan(float nofpclass(snan))
declare nofpclass(snan) float @nofpclass_snan(float nofpclass(snan))
@@ -2113,7 +2116,8 @@ define float @nofpclass_callsites(float %arg) {
; CHECK: attributes #49 = { nosanitize_bounds }
; CHECK: attributes #50 = { allockind("alloc,uninitialized") }
; CHECK: attributes #51 = { sanitize_numerical_stability }
-; CHECK: attributes #52 = { builtin }
+; CHECK: attributes #52 = { nonblocking }
+; CHECK: attributes #53 = { builtin }
;; Metadata
diff --git a/llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll
new file mode 100644
index 0000000000000..222df484273c7
--- /dev/null
+++ b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll
@@ -0,0 +1,35 @@
+; RUN: opt < %s -passes=rtsan -S | FileCheck %s
+
+; Function Attrs: mustprogress noinline nonblocking optnone ssp uwtable(sync)
+define void @violation() #0 {
+ %1 = alloca ptr, align 8
+ %2 = call ptr @malloc(i64 noundef 2) #3
+ store ptr %2, ptr %1, align 8
+ ret void
+}
+
+; Function Attrs: allocsize(0)
+declare ptr @malloc(i64 noundef) #1
+
+; Function Attrs: mustprogress noinline norecurse optnone ssp uwtable(sync)
+define noundef i32 @main() #2 {
+ %1 = alloca i32, align 4
+ store i32 0, ptr %1, align 4
+ call void @violation() #4
+ ret i32 0
+}
+
+attributes #0 = { mustprogress noinline nonblocking optnone ssp uwtable(sync) }
+attributes #1 = { allocsize(0) }
+attributes #2 = { mustprogress noinline norecurse optnone ssp uwtable(sync) }
+attributes #3 = { allocsize(0) }
+attributes #4 = { nonblocking }
+
+; RealtimeSanitizer pass should insert __rtsan_realtime_enter right after function definition
+; CHECK: define{{.*}}violation
+; CHECK-NEXT: call{{.*}}__rtsan_realtime_enter
+
+; RealtimeSanitizer pass should insert __rtsan_realtime_exit right before function return
+; CHECK: call{{.*}}@__rtsan_realtime_exit
+; CHECK-NEXT: ret{{.*}}void
+
More information about the llvm-commits
mailing list