diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1344,11 +1344,6 @@ /*CompileKernel=*/true, /*Recover=*/true)); } - if (CodeGenOpts.OptimizationLevel > 0 && - LangOpts.Sanitize.has(SanitizerKind::MemTag)) { - MPM.addPass(StackSafetyGlobalAnnotatorPass()); - } - if (CodeGenOpts.OptimizationLevel == 0) { addCoroutinePassesAtO0(MPM, LangOpts, CodeGenOpts); addSanitizersAtO0(MPM, TargetTriple, LangOpts, CodeGenOpts); diff --git a/clang/test/Driver/memtag.c b/clang/test/Driver/memtag.c --- a/clang/test/Driver/memtag.c +++ b/clang/test/Driver/memtag.c @@ -1,23 +1,27 @@ // REQUIRES: aarch64-registered-target // Old pass manager. -// RUN: %clang -fno-experimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag %s -S -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-NO-SAFETY -// RUN: %clang -O1 -fno-experimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag %s -S -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-SAFETY -// RUN: %clang -O2 -fno-experimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag %s -S -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-SAFETY -// RUN: %clang -O3 -fno-experimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag %s -S -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-SAFETY +// RUN: %clang -fno-experimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-NO-SAFETY +// RUN: %clang -O1 -fno-experimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY +// RUN: %clang -O2 -fno-experimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY +// RUN: %clang -O3 -fno-experimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY // New pass manager. -// RUN: %clang -fexperimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag %s -S -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-NO-SAFETY -// RUN: %clang -O1 -fexperimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag %s -S -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-SAFETY -// RUN: %clang -O2 -fexperimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag %s -S -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-SAFETY -// RUN: %clang -O3 -fexperimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag %s -S -emit-llvm -o - | FileCheck %s --check-prefix=CHECK-SAFETY +// RUN: %clang -fexperimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-NO-SAFETY +// RUN: %clang -O1 -fexperimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY +// RUN: %clang -O2 -fexperimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY +// RUN: %clang -O3 -fexperimental-new-pass-manager -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s --check-prefix=CHECK-SAFETY int z; __attribute__((noinline)) void use(int *p) { *p = z; } -int foo() { int x; use(&x); return x; } +int foo() { + int x; + use(&x); + return x; +} -// CHECK-NO-SAFETY: define dso_local i32 @foo() -// CHECK-NO-SAFETY: %{{.*}} = alloca i32, align 4{{$}} +// CHECK-NO-SAFETY-NOT: allocas uses -// CHECK-SAFETY: define dso_local i32 @foo() -// CHECK-SAFETY: %{{.*}} = alloca i32, align 4, !stack-safe +// CHECK-SAFETY-LABEL: @foo +// CHECK-SAFETY-LABEL: allocas uses: +// CHECK-SAFETY-NEXT: [4]: [0,4) diff --git a/clang/test/Driver/memtag_lto.c b/clang/test/Driver/memtag_lto.c --- a/clang/test/Driver/memtag_lto.c +++ b/clang/test/Driver/memtag_lto.c @@ -1,117 +1,111 @@ // REQUIRES: aarch64-registered-target +// No MTE, so no StackSafety. + // RUN: rm -f %t* -// -O1, no tagging: both are unsafe. -// RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -S -emit-llvm -c %s -o - | FileCheck %s -// RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -S -emit-llvm -c %s -o - | FileCheck %s +// -O1, no tagging +// RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s +// RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s -// Full LTO: both are unsafe. +// Full LTO // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -c %s -flto=full -o %t.lto1.bc // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -c -DBUILD2 %s -flto=full -o %t.lto2.bc -// RUN: llvm-lto2 run -o %t.lto %t.lto1.bc %t.lto2.bc -save-temps -O1 \ +// RUN: llvm-lto2 run -o %t.lto %t.lto1.bc %t.lto2.bc -save-temps -stack-safety-print=1 -thinlto-threads 1 -O1 \ // RUN: -r %t.lto1.bc,fn,plx \ // RUN: -r %t.lto1.bc,use,lx \ // RUN: -r %t.lto1.bc,use_local,plx \ // RUN: -r %t.lto1.bc,w, \ // RUN: -r %t.lto2.bc,use,plx \ -// RUN: -r %t.lto2.bc,z, -// RUN: llvm-dis %t.lto.0.5.precodegen.bc -o - | FileCheck %s +// RUN: -r %t.lto2.bc,z, 2>&1 | FileCheck %s --allow-empty -// Full LTO, new PM: both are unsafe. +// Full LTO, new PM // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -c %s -flto=full -o %t.ltonewpm1.bc // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -c -DBUILD2 %s -flto=full -o %t.ltonewpm2.bc -// RUN: llvm-lto2 run -use-new-pm -o %t.ltonewpm %t.ltonewpm1.bc %t.ltonewpm2.bc -save-temps -O1 \ +// RUN: llvm-lto2 run -use-new-pm -o %t.ltonewpm %t.ltonewpm1.bc %t.ltonewpm2.bc -save-temps -stack-safety-print=1 -thinlto-threads 1 -O1 \ // RUN: -r %t.ltonewpm1.bc,fn,plx \ // RUN: -r %t.ltonewpm1.bc,use,lx \ // RUN: -r %t.ltonewpm1.bc,use_local,plx \ // RUN: -r %t.ltonewpm1.bc,w, \ // RUN: -r %t.ltonewpm2.bc,use,plx \ -// RUN: -r %t.ltonewpm2.bc,z, -// RUN: llvm-dis %t.ltonewpm.0.5.precodegen.bc -o - | FileCheck %s +// RUN: -r %t.ltonewpm2.bc,z, 2>&1 | FileCheck %s --allow-empty -// Thin LTO: both are unsafe. +// Thin LTO // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -c %s -flto=thin -o %t.thinlto1.bc // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -c -DBUILD2 %s -flto=thin -o %t.thinlto2.bc -// RUN: llvm-lto2 run -o %t.thinlto %t.thinlto1.bc %t.thinlto2.bc -save-temps -O1 \ +// RUN: llvm-lto2 run -o %t.thinlto %t.thinlto1.bc %t.thinlto2.bc -save-temps -stack-safety-print=1 -thinlto-threads 1 -O1 \ // RUN: -r %t.thinlto1.bc,fn,plx \ // RUN: -r %t.thinlto1.bc,use,lx \ // RUN: -r %t.thinlto1.bc,use_local,plx \ // RUN: -r %t.thinlto1.bc,w, \ // RUN: -r %t.thinlto2.bc,use,plx \ -// RUN: -r %t.thinlto2.bc,z, -// RUN: llvm-dis %t.thinlto.1.5.precodegen.bc -o - | FileCheck %s +// RUN: -r %t.thinlto2.bc,z, 2>&1 | FileCheck %s --allow-empty -// Thin LTO, new PM: both are unsafe. +// Thin LTO, new PM // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -c %s -flto=thin -o %t.thinltonewpm1.bc // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -c -DBUILD2 %s -flto=thin -o %t.thinltonewpm2.bc -// RUN: llvm-lto2 run -use-new-pm -o %t.thinltonewpm %t.thinltonewpm1.bc %t.thinltonewpm2.bc -save-temps -O1 \ +// RUN: llvm-lto2 run -use-new-pm -o %t.thinltonewpm %t.thinltonewpm1.bc %t.thinltonewpm2.bc -save-temps -stack-safety-print=1 -thinlto-threads 1 -O1 \ // RUN: -r %t.thinltonewpm1.bc,fn,plx \ // RUN: -r %t.thinltonewpm1.bc,use,lx \ // RUN: -r %t.thinltonewpm1.bc,use_local,plx \ // RUN: -r %t.thinltonewpm1.bc,w, \ // RUN: -r %t.thinltonewpm2.bc,use,plx \ -// RUN: -r %t.thinltonewpm2.bc,z, -// RUN: llvm-dis %t.thinltonewpm.1.5.precodegen.bc -o - | FileCheck %s +// RUN: -r %t.thinltonewpm2.bc,z, 2>&1 | FileCheck %s --allow-empty // Now with MTE. // RUN: rm -f %t* // -O0: both are unsafe. -// RUN: %clang -fno-experimental-new-pass-manager -O0 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -S -emit-llvm -c %s -o - | FileCheck %s -// RUN: %clang -fexperimental-new-pass-manager -O0 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -S -emit-llvm -c %s -o - | FileCheck %s +// RUN: %clang -fno-experimental-new-pass-manager -O0 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s +// RUN: %clang -fexperimental-new-pass-manager -O0 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o - 2>&1 | FileCheck %s // No LTO: just one is safe. -// RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -S -emit-llvm -c %s -o - | FileCheck %s -check-prefixes=XUNSAFE,YSAFE -// RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -S -emit-llvm -c %s -o - | FileCheck %s -check-prefixes=XUNSAFE,YSAFE +// RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o /dev/null 2>&1 | FileCheck %s -check-prefixes=SSI,XUNSAFE,YSAFE +// RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -mllvm -stack-safety-print=1 %s -S -o /dev/null 2>&1 | FileCheck %s -check-prefixes=SSI,XUNSAFE,YSAFE -// FIXME: Full LTO: both are safe. +// Full LTO: both are safe. // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c %s -flto=full -o %t.lto1.bc // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c -DBUILD2 %s -flto=full -o %t.lto2.bc -// RUN: llvm-lto2 run -o %t.lto %t.lto1.bc %t.lto2.bc -save-temps -O1 \ +// RUN: llvm-lto2 run -o %t.lto %t.lto1.bc %t.lto2.bc -save-temps -stack-safety-print=1 -thinlto-threads 1 -O1 \ // RUN: -r %t.lto1.bc,fn,plx \ // RUN: -r %t.lto1.bc,use,lx \ // RUN: -r %t.lto1.bc,use_local,plx \ // RUN: -r %t.lto1.bc,w, \ // RUN: -r %t.lto2.bc,use,plx \ -// RUN: -r %t.lto2.bc,z, -// RUN: llvm-dis %t.lto.0.5.precodegen.bc -o - | FileCheck %s -check-prefixes=XUNSAFE,YSAFE +// RUN: -r %t.lto2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XSAFE,YSAFE -// FIXME: Full LTO, new PM: both are safe. +// Full LTO, new PM: both are safe. // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c %s -flto=full -o %t.ltonewpm1.bc // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c -DBUILD2 %s -flto=full -o %t.ltonewpm2.bc -// RUN: llvm-lto2 run -use-new-pm -o %t.ltonewpm %t.ltonewpm1.bc %t.ltonewpm2.bc -save-temps -O1 \ +// RUN: llvm-lto2 run -use-new-pm -o %t.ltonewpm %t.ltonewpm1.bc %t.ltonewpm2.bc -save-temps -stack-safety-print=1 -thinlto-threads 1 -O1 \ // RUN: -r %t.ltonewpm1.bc,fn,plx \ // RUN: -r %t.ltonewpm1.bc,use,lx \ // RUN: -r %t.ltonewpm1.bc,use_local,plx \ // RUN: -r %t.ltonewpm1.bc,w, \ // RUN: -r %t.ltonewpm2.bc,use,plx \ -// RUN: -r %t.ltonewpm2.bc,z, -// RUN: llvm-dis %t.ltonewpm.0.5.precodegen.bc -o - | FileCheck %s -check-prefixes=XUNSAFE,YSAFE +// RUN: -r %t.ltonewpm2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XSAFE,YSAFE // FIXME: Thin LTO: both are safe. // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c %s -flto=thin -o %t.thinlto1.bc // RUN: %clang -fno-experimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c -DBUILD2 %s -flto=thin -o %t.thinlto2.bc -// RUN: llvm-lto2 run -o %t.thinlto %t.thinlto1.bc %t.thinlto2.bc -save-temps -O1 \ +// RUN: llvm-lto2 run -o %t.thinlto %t.thinlto1.bc %t.thinlto2.bc -save-temps -stack-safety-print=1 -thinlto-threads 1 -O1 \ // RUN: -r %t.thinlto1.bc,fn,plx \ // RUN: -r %t.thinlto1.bc,use,lx \ // RUN: -r %t.thinlto1.bc,use_local,plx \ // RUN: -r %t.thinlto1.bc,w, \ // RUN: -r %t.thinlto2.bc,use,plx \ -// RUN: -r %t.thinlto2.bc,z, -// RUN: llvm-dis %t.thinlto.1.5.precodegen.bc -o - | FileCheck %s -check-prefixes=XUNSAFE,YSAFE +// RUN: -r %t.thinlto2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XUNSAFE,YSAFE // FIXME: Thin LTO, new PM: both are safe. // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c %s -flto=thin -o %t.thinltonewpm1.bc // RUN: %clang -fexperimental-new-pass-manager -O1 -target aarch64-unknown-linux -march=armv8+memtag -fsanitize=memtag -c -DBUILD2 %s -flto=thin -o %t.thinltonewpm2.bc -// RUN: llvm-lto2 run -use-new-pm -o %t.thinltonewpm %t.thinltonewpm1.bc %t.thinltonewpm2.bc -save-temps -O1 \ +// RUN: llvm-lto2 run -use-new-pm -o %t.thinltonewpm %t.thinltonewpm1.bc %t.thinltonewpm2.bc -save-temps -stack-safety-print=1 -thinlto-threads 1 -O1 \ // RUN: -r %t.thinltonewpm1.bc,fn,plx \ // RUN: -r %t.thinltonewpm1.bc,use,lx \ // RUN: -r %t.thinltonewpm1.bc,use_local,plx \ // RUN: -r %t.thinltonewpm1.bc,w, \ // RUN: -r %t.thinltonewpm2.bc,use,plx \ -// RUN: -r %t.thinltonewpm2.bc,z, -// RUN: llvm-dis %t.thinltonewpm.1.5.precodegen.bc -o - | FileCheck %s -check-prefixes=XUNSAFE,YSAFE +// RUN: -r %t.thinltonewpm2.bc,z, 2>&1 | FileCheck %s -check-prefixes=SSI,XUNSAFE,YSAFE void use(int *p); @@ -125,19 +119,21 @@ char w; __attribute__((noinline)) void use_local(char *p) { *p = w; } +// SSI-LABEL: @fn +// SSI-LABEL: allocas uses: __attribute__((visibility("default"))) int fn() { - // XUNSAFE: alloca i32, align 4{{$}} - // XSAFE: alloca i32, align 4, !stack-safe + // XUNSAFE-DAG: [4]: full-set + // XSAFE-DAG: [4]: [0,4) int x; use(&x); - // YUNSAFE-NEXT: alloca i8, align 4{{$}} - // YSAFE-NEXT: alloca i8, align 4, !stack-safe + // YUNSAFE-DAG: [1]: full-set + // YSAFE-DAG: [1]: [0,1) char y; use_local(&y); return x + y; } -// CHECK-NOT: !stack-safe +// CHECK-NOT: allocas uses: #endif diff --git a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h --- a/llvm/include/llvm/Analysis/StackSafetyAnalysis.h +++ b/llvm/include/llvm/Analysis/StackSafetyAnalysis.h @@ -62,7 +62,7 @@ StackSafetyGlobalInfo &operator=(StackSafetyGlobalInfo &&); ~StackSafetyGlobalInfo(); - bool setMetadata(Module &M) const; + bool isSafe(const AllocaInst &AI) const; void print(raw_ostream &O) const; void dump() const; }; @@ -124,14 +124,6 @@ PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); }; -class StackSafetyGlobalAnnotatorPass - : public PassInfoMixin { - -public: - explicit StackSafetyGlobalAnnotatorPass() {} - PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); -}; - /// This pass performs the global (interprocedural) stack safety analysis /// (legacy pass manager). class StackSafetyGlobalInfoWrapperPass : public ModulePass { diff --git a/llvm/lib/Analysis/StackSafetyAnalysis.cpp b/llvm/lib/Analysis/StackSafetyAnalysis.cpp --- a/llvm/lib/Analysis/StackSafetyAnalysis.cpp +++ b/llvm/lib/Analysis/StackSafetyAnalysis.cpp @@ -23,6 +23,7 @@ #include "llvm/Support/raw_ostream.h" #include #include +#include using namespace llvm; @@ -166,7 +167,7 @@ if (F) { size_t Pos = 0; for (auto &I : instructions(F)) { - if (const auto *AI = dyn_cast(&I)) { + if (const AllocaInst *AI = dyn_cast(&I)) { auto &AS = Allocas[Pos]; O << " " << AI->getName() << "[" << getStaticAllocaSizeRange(*AI).getUpper() << "]: " << AS << "\n"; @@ -189,6 +190,7 @@ struct StackSafetyGlobalInfo::InfoTy { GVToSSI Info; + std::unordered_set SafeAllocas; }; namespace { @@ -546,31 +548,6 @@ return Functions; } -bool setStackSafetyMetadata(Module &M, const GVToSSI &SSGI) { - bool Changed = false; - for (auto &F : M.functions()) { - if (F.isDeclaration() || F.hasOptNone()) - continue; - auto Iter = SSGI.find(&F); - if (Iter == SSGI.end()) - continue; - const FunctionInfo &Summary = Iter->second; - size_t Pos = 0; - for (auto &I : instructions(F)) { - if (auto *AI = dyn_cast(&I)) { - auto &AS = Summary.Allocas[Pos]; - if (getStaticAllocaSizeRange(*AI).contains(AS.Range)) { - AI->setMetadata(M.getMDKindID("stack-safe"), - MDNode::get(M.getContext(), None)); - Changed = true; - } - ++Pos; - } - } - } - return Changed; -} - const Function *findCalleeInModule(const GlobalValue *GV) { while (GV) { if (GV->isInterposable() || !GV->isDSOLocal()) @@ -683,7 +660,24 @@ Functions.emplace(&F, std::move(FI)); } } - Info.reset(new InfoTy{createGlobalStackSafetyInfo(std::move(Functions))}); + Info.reset( + new InfoTy{createGlobalStackSafetyInfo(std::move(Functions)), {}}); + for (auto &KV : Info->Info) { + if (!KV.first->isDeclaration()) { + size_t Pos = 0; + // FIXME: Convert FunctionInfo::Allocas into map + // and do not rely on alloca index. + for (auto &I : instructions(*cast(KV.first))) { + if (const auto &AI = dyn_cast(&I)) { + if (getStaticAllocaSizeRange(*AI).contains( + KV.second.Allocas[Pos].Range)) { + Info->SafeAllocas.insert(AI); + } + ++Pos; + } + } + } + } if (StackSafetyPrint) print(errs()); } @@ -707,8 +701,9 @@ StackSafetyGlobalInfo::~StackSafetyGlobalInfo() = default; -bool StackSafetyGlobalInfo::setMetadata(Module &M) const { - return setStackSafetyMetadata(M, getInfo().Info); +bool StackSafetyGlobalInfo::isSafe(const AllocaInst &AI) const { + const auto &Info = getInfo(); + return Info.SafeAllocas.find(&AI) != Info.SafeAllocas.end(); } void StackSafetyGlobalInfo::print(raw_ostream &O) const { @@ -781,13 +776,6 @@ return PreservedAnalyses::all(); } -PreservedAnalyses -StackSafetyGlobalAnnotatorPass::run(Module &M, ModuleAnalysisManager &AM) { - auto &SSGI = AM.getResult(M); - SSGI.setMetadata(M); - return PreservedAnalyses::all(); -} - char StackSafetyGlobalInfoWrapperPass::ID = 0; StackSafetyGlobalInfoWrapperPass::StackSafetyGlobalInfoWrapperPass() @@ -805,6 +793,7 @@ void StackSafetyGlobalInfoWrapperPass::getAnalysisUsage( AnalysisUsage &AU) const { + AU.setPreservesAll(); AU.addRequired(); } @@ -812,7 +801,7 @@ SSGI = {&M, [this](Function &F) -> const StackSafetyInfo & { return getAnalysis(F).getResult(); }}; - return SSGI.setMetadata(M); + return false; } ModulePass *llvm::createStackSafetyGlobalInfoWrapperPass() { @@ -829,7 +818,7 @@ static const char GlobalPassName[] = "Stack Safety Analysis"; INITIALIZE_PASS_BEGIN(StackSafetyGlobalInfoWrapperPass, DEBUG_TYPE, - GlobalPassName, false, false) + GlobalPassName, false, true) INITIALIZE_PASS_DEPENDENCY(StackSafetyInfoWrapperPass) INITIALIZE_PASS_END(StackSafetyGlobalInfoWrapperPass, DEBUG_TYPE, - GlobalPassName, false, false) + GlobalPassName, false, true) diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -96,7 +96,6 @@ MODULE_PASS("kasan-module", ModuleAddressSanitizerPass(/*CompileKernel=*/true, false, true, false)) MODULE_PASS("sancov-module", ModuleSanitizerCoveragePass()) MODULE_PASS("poison-checking", PoisonCheckingPass()) -MODULE_PASS("stack-safety-annotator", StackSafetyGlobalAnnotatorPass()) #undef MODULE_PASS #ifndef CGSCC_ANALYSIS diff --git a/llvm/lib/Target/AArch64/AArch64.h b/llvm/lib/Target/AArch64/AArch64.h --- a/llvm/lib/Target/AArch64/AArch64.h +++ b/llvm/lib/Target/AArch64/AArch64.h @@ -58,7 +58,7 @@ AArch64Subtarget &, AArch64RegisterBankInfo &); FunctionPass *createAArch64PreLegalizeCombiner(bool IsOptNone); FunctionPass *createAArch64PostLegalizeCombiner(bool IsOptNone); -FunctionPass *createAArch64StackTaggingPass(bool MergeInit); +FunctionPass *createAArch64StackTaggingPass(bool IsOptNone); FunctionPass *createAArch64StackTaggingPreRAPass(); void initializeAArch64A53Fix835769Pass(PassRegistry&); diff --git a/llvm/lib/Target/AArch64/AArch64StackTagging.cpp b/llvm/lib/Target/AArch64/AArch64StackTagging.cpp --- a/llvm/lib/Target/AArch64/AArch64StackTagging.cpp +++ b/llvm/lib/Target/AArch64/AArch64StackTagging.cpp @@ -23,6 +23,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/ScalarEvolutionExpressions.h" +#include "llvm/Analysis/StackSafetyAnalysis.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/CodeGen/LiveRegUnits.h" #include "llvm/CodeGen/MachineBasicBlock.h" @@ -44,6 +45,7 @@ #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/IntrinsicsAArch64.h" #include "llvm/IR/Metadata.h" +#include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Debug.h" @@ -61,6 +63,11 @@ "stack-tagging-merge-init", cl::Hidden, cl::init(true), cl::ZeroOrMore, cl::desc("merge stack variable initializers with tagging when possible")); +static cl::opt + ClUseStackSafety("stack-tagging-use-stack-safety", cl::Hidden, + cl::init(true), cl::ZeroOrMore, + cl::desc("Use Stack Safety analysis results")); + static cl::opt ClScanLimit("stack-tagging-merge-init-scan-limit", cl::init(40), cl::Hidden); @@ -275,15 +282,17 @@ int Tag; // -1 for non-tagged allocations }; - bool MergeInit; + const bool MergeInit; + const bool UseStackSafety; public: static char ID; // Pass ID, replacement for typeid - AArch64StackTagging(bool MergeInit = true) + AArch64StackTagging(bool IsOptNone = false) : FunctionPass(ID), - MergeInit(ClMergeInit.getNumOccurrences() > 0 ? ClMergeInit - : MergeInit) { + MergeInit(ClMergeInit.getNumOccurrences() ? ClMergeInit : !IsOptNone), + UseStackSafety(ClUseStackSafety.getNumOccurrences() ? ClUseStackSafety + : !IsOptNone) { initializeAArch64StackTaggingPass(*PassRegistry::getPassRegistry()); } @@ -305,13 +314,16 @@ StringRef getPassName() const override { return "AArch64 Stack Tagging"; } private: - Function *F; - Function *SetTagFunc; - const DataLayout *DL; - AAResults *AA; + Function *F = nullptr; + Function *SetTagFunc = nullptr; + const DataLayout *DL = nullptr; + AAResults *AA = nullptr; + const StackSafetyGlobalInfo *SSI = nullptr; void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesCFG(); + if (UseStackSafety) + AU.addRequired(); if (MergeInit) AU.addRequired(); } @@ -323,11 +335,13 @@ INITIALIZE_PASS_BEGIN(AArch64StackTagging, DEBUG_TYPE, "AArch64 Stack Tagging", false, false) +INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass) +INITIALIZE_PASS_DEPENDENCY(StackSafetyGlobalInfoWrapperPass) INITIALIZE_PASS_END(AArch64StackTagging, DEBUG_TYPE, "AArch64 Stack Tagging", false, false) -FunctionPass *llvm::createAArch64StackTaggingPass(bool MergeInit) { - return new AArch64StackTagging(MergeInit); +FunctionPass *llvm::createAArch64StackTaggingPass(bool IsOptNone) { + return new AArch64StackTagging(IsOptNone); } Instruction *AArch64StackTagging::collectInitializers(Instruction *StartInst, @@ -402,7 +416,7 @@ // swifterror allocas are register promoted by ISel !AI.isSwiftError() && // safe allocas are not interesting - !AI.getMetadata("stack-safe"); + !(SSI && SSI->isSafe(AI)); return IsInteresting; } @@ -518,6 +532,8 @@ if (!Fn.hasFnAttribute(Attribute::SanitizeMemTag)) return false; + if (UseStackSafety) + SSI = &getAnalysis().getResult(); F = &Fn; DL = &Fn.getParent()->getDataLayout(); if (MergeInit) diff --git a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp --- a/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -486,8 +486,8 @@ addPass(createLICMPass()); } - addPass(createAArch64StackTaggingPass(/* MergeInit = */ TM->getOptLevel() != - CodeGenOpt::None)); + addPass(createAArch64StackTaggingPass( + /*IsOptNone=*/TM->getOptLevel() == CodeGenOpt::None)); // Add Control Flow Guard checks. if (TM->getTargetTriple().isOSWindows()) diff --git a/llvm/test/Analysis/StackSafetyAnalysis/ipa-attr.ll b/llvm/test/Analysis/StackSafetyAnalysis/ipa-attr.ll deleted file mode 100644 --- a/llvm/test/Analysis/StackSafetyAnalysis/ipa-attr.ll +++ /dev/null @@ -1,34 +0,0 @@ -; RUN: llvm-as %s -o %t0.bc -; RUN: llvm-as %S/Inputs/ipa.ll -o %t1.bc -; RUN: llvm-link -disable-lazy-loading %t0.bc %t1.bc -o %t.combined.bc -; RUN: opt -S -passes="stack-safety-annotator" %t.combined.bc -o - 2>&1 | FileCheck %s - -target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux-gnu" - -declare void @Write1(i8* %p) -declare void @Write8(i8* %p) - -; Basic out-of-bounds. -define void @f1() { -; CHECK-LABEL: define void @f1() { -; CHECK: alloca i32, align 4{{$}} -entry: - %x = alloca i32, align 4 - %x1 = bitcast i32* %x to i8* - call void @Write8(i8* %x1) - ret void -} - -; Basic in-bounds. -define void @f2() { -; CHECK-LABEL: define void @f2() { -; CHECK: alloca i32, align 4, !stack-safe ![[A:[0-9]+]]{{$}} -entry: - %x = alloca i32, align 4 - %x1 = bitcast i32* %x to i8* - call void @Write1(i8* %x1) - ret void -} - -; CHECK: ![[A]] = !{} diff --git a/llvm/test/CodeGen/AArch64/O3-pipeline.ll b/llvm/test/CodeGen/AArch64/O3-pipeline.ll --- a/llvm/test/CodeGen/AArch64/O3-pipeline.ll +++ b/llvm/test/CodeGen/AArch64/O3-pipeline.ll @@ -66,6 +66,14 @@ ; CHECK-NEXT: Interleaved Load Combine Pass ; CHECK-NEXT: Dominator Tree Construction ; CHECK-NEXT: Interleaved Access Pass +; CHECK-NEXT: Stack Safety Analysis +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Stack Safety Local Analysis +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction ; CHECK-NEXT: Basic Alias Analysis (stateless AA impl) ; CHECK-NEXT: Function Alias Analysis Results ; CHECK-NEXT: AArch64 Stack Tagging @@ -200,6 +208,17 @@ ; CHECK-NEXT: Pass Arguments: -domtree ; CHECK-NEXT: FunctionPass Manager ; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Pass Arguments: -assumption-cache-tracker -targetlibinfo -domtree -loops -scalar-evolution -stack-safety-local +; CHECK-NEXT: Assumption Cache Tracker +; CHECK-NEXT: Target Library Information +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction +; CHECK-NEXT: Natural Loop Information +; CHECK-NEXT: Scalar Evolution Analysis +; CHECK-NEXT: Stack Safety Local Analysis +; CHECK-NEXT: Pass Arguments: -domtree +; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Dominator Tree Construction define void @f() { ret void diff --git a/llvm/test/CodeGen/AArch64/stack-tagging.ll b/llvm/test/CodeGen/AArch64/stack-tagging.ll --- a/llvm/test/CodeGen/AArch64/stack-tagging.ll +++ b/llvm/test/CodeGen/AArch64/stack-tagging.ll @@ -1,4 +1,5 @@ -; RUN: opt < %s -stack-tagging -S -o - | FileCheck %s +; RUN: opt < %s -stack-tagging -S -o - | FileCheck %s --check-prefixes=CHECK,SSI +; RUN: opt < %s -stack-tagging -stack-tagging-use-stack-safety=0 -S -o - | FileCheck %s --check-prefixes=CHECK,NOSSI target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" target triple = "aarch64--linux-android" @@ -8,6 +9,11 @@ declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) +define dso_local void @noUse32(i32*) sanitize_memtag { +entry: + ret void +} + define void @OneVar() sanitize_memtag { entry: %x = alloca i32, align 4 @@ -33,7 +39,7 @@ %x1 = alloca i32, align 4 %x2 = alloca i8, align 4 %x3 = alloca i32, i32 11, align 4 - %x4 = alloca i32, align 4, !stack-safe !0 + %x4 = alloca i32, align 4 call void @use32(i32* %x1) call void @use8(i8* %x2) call void @use32(i32* %x3) @@ -50,9 +56,12 @@ ; CHECK: alloca { [11 x i32], [4 x i8] }, align 16 ; CHECK: call { [11 x i32], [4 x i8] }* @llvm.aarch64.tagp.{{.*}}({ [11 x i32], [4 x i8] }* {{.*}}, i64 2) ; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 48) -; CHECK: alloca i32, align 4 -; CHECK-NOT: @llvm.aarch64.tagp -; CHECK-NOT: @llvm.aarch64.settag +; SSI: alloca i32, align 4 +; NOSSI: alloca { i32, [12 x i8] }, align 16 +; NOSSI: @llvm.aarch64.tagp. +; NOSSI: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16) +; SSI-NOT: @llvm.aarch64.tagp +; SSI-NOT: @llvm.aarch64.settag ; CHECK: call void @use32( ; CHECK: call void @use8( @@ -61,6 +70,7 @@ ; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16) ; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16) ; CHECK: call void @llvm.aarch64.settag(i8* {{.*}}, i64 48) +; NOSSI: call void @llvm.aarch64.settag(i8* {{.*}}, i64 16) ; CHECK-NEXT: ret void @@ -161,12 +171,15 @@ another_bb: call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cz) store i32 7, i32* %z + call void @noUse32(i32* %z) call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cz) call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cz) store i32 7, i32* %z call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cz) call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cxcy) store i32 8, i32* %xy + call void @noUse32(i32* %x) + call void @noUse32(i32* %y) call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cxcy) ret void } @@ -179,15 +192,18 @@ ; CHECK: alloca { i32, [12 x i8] }, align 16 ; CHECK: call { i32, [12 x i8] }* @llvm.aarch64.tagp ; CHECK: call void @llvm.aarch64.settag( -; CHECK: alloca { i32, [12 x i8] }, align 16 -; CHECK: call { i32, [12 x i8] }* @llvm.aarch64.tagp -; CHECK: call void @llvm.aarch64.settag( +; SSI: alloca i32, align 4 +; NOSSI: alloca { i32, [12 x i8] }, align 16 +; NOSSI: call { i32, [12 x i8] }* @llvm.aarch64.tagp +; NOSSI: call void @llvm.aarch64.settag( ; CHECK: store i32 +; CHECK: call void @noUse32(i32* ; CHECK: store i32 ; CHECK: store i32 +; CHECK: call void @noUse32(i32* ; CHECK: call void @llvm.aarch64.settag( ; CHECK: call void @llvm.aarch64.settag( -; CHECK: call void @llvm.aarch64.settag( +; NOSSI: call void @llvm.aarch64.settag( ; CHECK: ret void !0 = !{} \ No newline at end of file