Skip to content

[LLVM][rtsan] Add sanitize_realtime attribute for the realtime sanitizer #100596

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 8, 2024

Conversation

cjappl
Copy link
Contributor

@cjappl cjappl commented Jul 25, 2024

Add a new "sanitize_realtime" attribute, which will correspond to the sanitize_realtime function effect in clang. This is used in the realtime sanitizer transform

Please see the reviewer support document for what our next steps are. The original discourse thread can be found here

@llvmbot
Copy link
Member

llvmbot commented Jul 25, 2024

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Chris Apple (cjappl)

Changes

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 for what our next steps are. The original discourse thread can be found here


Full diff: https://github.com/llvm/llvm-project/pull/100596.diff

15 Files Affected:

  • (modified) llvm/CODE_OWNERS.TXT (+4)
  • (modified) llvm/include/llvm/Bitcode/LLVMBitCodes.h (+1)
  • (modified) llvm/include/llvm/IR/Attributes.td (+3)
  • (added) llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h (+35)
  • (added) llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h (+17)
  • (modified) llvm/lib/Bitcode/Reader/BitcodeReader.cpp (+2)
  • (modified) llvm/lib/Bitcode/Writer/BitcodeWriter.cpp (+2)
  • (modified) llvm/lib/Passes/PassBuilder.cpp (+6)
  • (modified) llvm/lib/Passes/PassRegistry.def (+4)
  • (modified) llvm/lib/Transforms/Instrumentation/CMakeLists.txt (+1)
  • (added) llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp (+50)
  • (modified) llvm/lib/Transforms/Utils/CodeExtractor.cpp (+1)
  • (modified) llvm/test/Bitcode/attributes.ll (+7)
  • (modified) llvm/test/Bitcode/compatibility.ll (+6-2)
  • (added) llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll (+35)
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: [email protected]
 D: SPIR-V backend (lib/Target/SPIRV/*)
+
+N: Christopher Apple, David Trevelyan
+E: [email protected], [email protected]
+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
+

@llvmbot
Copy link
Member

llvmbot commented Jul 25, 2024

@llvm/pr-subscribers-llvm-ir

Author: Chris Apple (cjappl)

Changes

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 for what our next steps are. The original discourse thread can be found here


Full diff: https://github.com/llvm/llvm-project/pull/100596.diff

15 Files Affected:

  • (modified) llvm/CODE_OWNERS.TXT (+4)
  • (modified) llvm/include/llvm/Bitcode/LLVMBitCodes.h (+1)
  • (modified) llvm/include/llvm/IR/Attributes.td (+3)
  • (added) llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h (+35)
  • (added) llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizerOptions.h (+17)
  • (modified) llvm/lib/Bitcode/Reader/BitcodeReader.cpp (+2)
  • (modified) llvm/lib/Bitcode/Writer/BitcodeWriter.cpp (+2)
  • (modified) llvm/lib/Passes/PassBuilder.cpp (+6)
  • (modified) llvm/lib/Passes/PassRegistry.def (+4)
  • (modified) llvm/lib/Transforms/Instrumentation/CMakeLists.txt (+1)
  • (added) llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp (+50)
  • (modified) llvm/lib/Transforms/Utils/CodeExtractor.cpp (+1)
  • (modified) llvm/test/Bitcode/attributes.ll (+7)
  • (modified) llvm/test/Bitcode/compatibility.ll (+6-2)
  • (added) llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll (+35)
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: [email protected]
 D: SPIR-V backend (lib/Target/SPIRV/*)
+
+N: Christopher Apple, David Trevelyan
+E: [email protected], [email protected]
+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
+

@cjappl
Copy link
Contributor Author

cjappl commented Jul 25, 2024

CC for review @vitalybuka @MaskRay @Sirraide @AaronBallman (maybe you can help suggest if someone is more appropriate in LLVM, this is my first PR in this area)

Co-author @davidtrevelyan

Copy link
Collaborator

@vitalybuka vitalybuka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but better to move transforms and CODE_OWNERS into separate patches

@cjappl cjappl force-pushed the rtsan_llvm_pass branch from 695c74e to d314f93 Compare July 30, 2024 19:41
@cjappl cjappl changed the title [LLVM][rtsan] Add nonblocking Attribute and RealtimeSanitizer pass [LLVM][rtsan] Add nonblocking attribute for the realtime sanitizer Jul 30, 2024
@cjappl
Copy link
Contributor Author

cjappl commented Jul 30, 2024

I have updated the review, splitting it into three:

  1. The codeowners review [NFC][LLVM] Add RealtimeSanitizer LLVM code owners #101231
  2. This review introducing the attribute
  3. The review introducing the transform, which directly depends on this review [LLVM][rtsan] Add RealtimeSanitizer transform pass #101232

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing LangRef specification.

@nikic
Copy link
Contributor

nikic commented Jul 30, 2024

Can't say for sure without seeing the spec, but this is almost certainly the wrong name for the attribute. nonblocking would imply that "blocking" (for some rigorous definition thereof) is UB and LLVM can optimize with that knowledge. What you probably want is a sanitize_realtime attribute or something.

@cjappl
Copy link
Contributor Author

cjappl commented Jul 30, 2024

Can't say for sure without seeing the spec, but this is almost certainly the wrong name for the attribute. nonblocking would imply that "blocking" (for some rigorous definition thereof) is UB and LLVM can optimize with that knowledge. What you probably want is a sanitize_realtime attribute or something.

This name was chosen to match the name of the function effect it keys off of. This sanitizer works hand in hand with clang "function effects" that look like [[clang::nonblocking]] and [[clang::blocking]]
https://discourse.llvm.org/t/rfc-nolock-and-noalloc-attributes/76837

Eventually we will add LLVM IR attributes for blocking which will correspond to [[clang::blocking]] (so users can mark their own functions as not safe for nonblocking context and nosanitize_realtime (so users can disable this for certain functions)

A "reviewers guide" can be found here, describing how the sanitizer works:
https://github.com/realtime-sanitizer/radsan/blob/doc/review-support/doc/review.md

We will defer to people more experienced in LLVM what the appropriate choice is here!

EDIT: Posed this as a question in the discourse thread https://discourse.llvm.org/t/rfc-nolock-and-noalloc-attributes/76837/103?u=cjappl

@cjappl cjappl requested a review from nikic July 30, 2024 20:12
@cjappl
Copy link
Contributor Author

cjappl commented Jul 30, 2024

Missing LangRef specification.

Fixed in latest version! Thanks for catching

vitalybuka pushed a commit that referenced this pull request Jul 31, 2024
@cjappl cjappl changed the title [LLVM][rtsan] Add nonblocking attribute for the realtime sanitizer [LLVM][rtsan] Add sanitize_realtime attribute for the realtime sanitizer Aug 5, 2024
@cjappl
Copy link
Contributor Author

cjappl commented Aug 5, 2024

@nikic based on your suggestions, I decided to update this to the attribute sanitize_realtime. Let me know if this works for you or you want to see other changes.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cjappl
Copy link
Contributor Author

cjappl commented Aug 8, 2024

@nikic or @vitalybuka could we get a merge on this when you get a chance?

Thanks for everyone's review and discussion!

@nikic nikic merged commit b143b24 into llvm:main Aug 8, 2024
9 checks passed
@cjappl cjappl deleted the rtsan_llvm_pass branch August 8, 2024 13:42
MaskRay pushed a commit that referenced this pull request Aug 8, 2024
Split from #100596.

Introduce the RealtimeSanitizer transform, which inserts the
rtsan_enter/exit functions at the appropriate places in an instrumented
function.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants