Skip to content

Conversation

@gbMattN
Copy link
Contributor

@gbMattN gbMattN commented Nov 3, 2025

…nd update docs

@gbMattN gbMattN requested review from fhahn and melver November 3, 2025 14:45
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' labels Nov 3, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 3, 2025

@llvm/pr-subscribers-clang-driver

Author: Matthew Nagy (gbMattN)

Changes

…nd update docs


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

6 Files Affected:

  • (modified) clang/docs/TypeSanitizer.rst (+24-3)
  • (modified) clang/docs/UsersManual.rst (+9)
  • (modified) clang/include/clang/Driver/Options.td (+12)
  • (modified) clang/include/clang/Driver/SanitizerArgs.h (+2)
  • (modified) clang/lib/Driver/SanitizerArgs.cpp (+20)
  • (added) clang/test/CodeGen/sanitize-type-outlined.cpp (+23)
diff --git a/clang/docs/TypeSanitizer.rst b/clang/docs/TypeSanitizer.rst index 3c683a6c24bb4..6420d9e1d0ebd 100644 --- a/clang/docs/TypeSanitizer.rst +++ b/clang/docs/TypeSanitizer.rst @@ -20,9 +20,13 @@ code is build with ``-fno-strict-aliasing``, sacrificing performance. TypeSanitizer is built to catch when these strict aliasing rules have been violated, helping users find where such bugs originate in their code despite the code looking valid at first glance. -As TypeSanitizer is still experimental, it can currently have a large impact on runtime speed,  -memory use, and code size. It also has a large compile-time overhead. Work is being done to  -reduce these impacts. +Typical memory overhead introduced by TypeSanitizer is about **8x**. Runtime slowdown varies greatly +depending on how often the instrumented code relies on type aliasing. In the best case slowdown is +**2x-3x**. + +The compiler instrumentation also has an impact on code size and compilation overhead. There is an +experimental :ref:`instrumentation outlining option<outlining_flag>` which can greatly reduce this +but this may decrease runtime performance. The TypeSanitizer Algorithm =========================== @@ -128,6 +132,23 @@ references to LLVM IR specific terms. Sanitizer features ================== +.. _outlining_flag: + +Instrumentation code outlining +------------------------------ + +By default TypeSanitizer inlines the instrumentation code. This leads to increased +binary size and compilation time. Using the clang flag +``-fsanitize-type-outline-instrumentation`` (default: ``false``) +forces all code instrumentation to be outlined. This reduces the size of the +generated code and reduces compile-time overhead, but it also reduces runtime +performance. + +This outlined instrumentation is new. If you wish to verify that the outlined instrumentation +is behaving in the same way as the inline instrumentation, you can force TypeSanitizer +to use both types of instrumentation. You can use the clang flag +``-fsanitize-type-verify-outlined-instrumentation`` (default: ``false``) to do this. + ``__has_feature(type_sanitizer)`` ------------------------------------ diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index fb22ad3c90af4..04da152bd0aff 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2277,6 +2277,15 @@ are listed below. See :doc: `AddressSanitizer` for more details. +.. option:: -f[no-]sanitize-type-outline-instrumentation + + Controls how type sanitizer code is generated. If enabled will always use + a function call instead of inlining the code. Turning this option on may + reduce the binary size and compilation overhead, but might result in a worse + run-time performance. + + See :doc: `TypeSanitizer` for more details. + .. option:: -f[no-]sanitize-stats Enable simple statistics gathering for the enabled sanitizers. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 6e1c9425d8d75..adef9a592b55e 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2454,6 +2454,18 @@ def fsanitize_address_outline_instrumentation : Flag<["-"], "fsanitize-address-o def fno_sanitize_address_outline_instrumentation : Flag<["-"], "fno-sanitize-address-outline-instrumentation">, Group<f_clang_Group>, HelpText<"Use default code inlining logic for the address sanitizer">; +def fsanitize_type_outline_instrumentation : Flag<["-"], "fsanitize-type-outline-instrumentation">, + Group<f_clang_Group>, + HelpText<"Always generate function calls for type sanitizer instrumentation">; +def fno_sanitize_type_outline_instrumentation : Flag<["-"], "fno-sanitize-type-outline-instrumentation">, + Group<f_clang_Group>, + HelpText<"Use default code inlining logic for the type sanitizer">; +def fsanitize_type_verify_outlined_instrumentation : Flag<["-"], "fsanitize_type_verify_outlined_instrumentation">, + Group<f_clang_Group>, + HelpText<"Use both inlined and outlined instrumentation for type sanitizer to verify equivilence">; +def fno_sanitize_type_verify_outlined_instrumentation : Flag<["-"], "fno_sanitize_type_verify_outlined_instrumentation">, + Group<f_clang_Group>, + HelpText<"Don't use both inlined and outlined instrumentation for type sanitizer to verify equivilence">; defm sanitize_stable_abi : OptInCC1FFlag<"sanitize-stable-abi", "Stable ", "Conventional ", "ABI instrumentation for sanitizer runtime. Default: Conventional">; diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index eea7897e96afd..29569bcf7fa25 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -67,6 +67,8 @@ class SanitizerArgs { bool TsanFuncEntryExit = true; bool TsanAtomics = true; bool MinimalRuntime = false; + bool TysanOutlineInstrumentation = false; + bool TysanVerifyOutlinedInstrumentation = false; // True if cross-dso CFI support if provided by the system (i.e. Android). bool ImplicitCfiRuntime = false; bool NeedsMemProfRt = false; diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 5dd48f53b9069..c106dd63c2805 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -1176,6 +1176,17 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, options::OPT_fno_sanitize_alloc_token_extended, AllocTokenExtended); } + if (AllAddedKinds & SanitizerKind::Type) { + TysanOutlineInstrumentation = + Args.hasFlag(options::OPT_fsanitize_type_outline_instrumentation, + options::OPT_fno_sanitize_type_outline_instrumentation, + TysanOutlineInstrumentation); + TysanVerifyOutlinedInstrumentation = Args.hasFlag( + options::OPT_fsanitize_type_verify_outlined_instrumentation, + options::OPT_fno_sanitize_type_verify_outlined_instrumentation, + TysanVerifyOutlinedInstrumentation); + } + LinkRuntimes = Args.hasFlag(options::OPT_fsanitize_link_runtime, options::OPT_fno_sanitize_link_runtime, !Args.hasArg(options::OPT_r)); @@ -1500,6 +1511,15 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, CmdArgs.push_back("-asan-instrumentation-with-call-threshold=0"); } + if (TysanOutlineInstrumentation || TysanVerifyOutlinedInstrumentation) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-tysan-outline-instrumentation"); + } + if (TysanVerifyOutlinedInstrumentation) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-tysan-verify-outlined-instrumentation"); + } + // When emitting Stable ABI instrumentation, force outlining calls and avoid // inlining shadow memory poisoning. While this is a big performance burden // for now it allows full abstraction from implementation details. diff --git a/clang/test/CodeGen/sanitize-type-outlined.cpp b/clang/test/CodeGen/sanitize-type-outlined.cpp new file mode 100644 index 0000000000000..3c22f15c93d94 --- /dev/null +++ b/clang/test/CodeGen/sanitize-type-outlined.cpp @@ -0,0 +1,23 @@ +// RUN: %clang -S -fsanitize=type -emit-llvm -o - -fsanitize=type %s \ +// RUN: | FileCheck %s --check-prefixes=CHECK-NO-OUTLINE +// RUN: %clang -S -fsanitize=type -emit-llvm -o - -fsanitize=type %s \ +// RUN: -fsanitize-type-outline-instrumentation \ +// RUN: | FileCheck %s --check-prefixes=CHECK-OUTLINE + +// RUN: %clang -S -fsanitize=type -emit-llvm -o - -fsanitize=type %s \ +// RUN: -fsanitize-type-outline-instrumentation \ +// RUN: -fsanitize-type-verify-outlined-instrumentation \ +// RUN: | FileCheck %s --check-prefixes=CHECK-OUTLINE-VERIFY +// RUN: %clang -S -fsanitize=type -emit-llvm -o - -fsanitize=type %s \ +// RUN: -fsanitize-type-verify-outlined-instrumentation \ +// RUN: | FileCheck %s --check-prefixes=CHECK-VERIFY + +// CHECK-NO-OUTLINE-NOT: call{{.*}}@__tysan_instrument_mem_inst +// CHECK-OUTLINE: call{{.*}}@__tysan_instrument_mem_inst +// CHECK-OUTLINE-VERIFY: call{{.*}}@__tysan_instrument_mem_inst +// CHECK-VERIFY: call{{.*}}@__tysan_instrument_mem_inst + +float alias(int *ptr){ + float *aliasedPtr = (float *)ptr; + return *aliasedPtr; +} 
@llvmbot
Copy link
Member

llvmbot commented Nov 3, 2025

@llvm/pr-subscribers-clang

Author: Matthew Nagy (gbMattN)

Changes

…nd update docs


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

6 Files Affected:

  • (modified) clang/docs/TypeSanitizer.rst (+24-3)
  • (modified) clang/docs/UsersManual.rst (+9)
  • (modified) clang/include/clang/Driver/Options.td (+12)
  • (modified) clang/include/clang/Driver/SanitizerArgs.h (+2)
  • (modified) clang/lib/Driver/SanitizerArgs.cpp (+20)
  • (added) clang/test/CodeGen/sanitize-type-outlined.cpp (+23)
diff --git a/clang/docs/TypeSanitizer.rst b/clang/docs/TypeSanitizer.rst index 3c683a6c24bb4..6420d9e1d0ebd 100644 --- a/clang/docs/TypeSanitizer.rst +++ b/clang/docs/TypeSanitizer.rst @@ -20,9 +20,13 @@ code is build with ``-fno-strict-aliasing``, sacrificing performance. TypeSanitizer is built to catch when these strict aliasing rules have been violated, helping users find where such bugs originate in their code despite the code looking valid at first glance. -As TypeSanitizer is still experimental, it can currently have a large impact on runtime speed,  -memory use, and code size. It also has a large compile-time overhead. Work is being done to  -reduce these impacts. +Typical memory overhead introduced by TypeSanitizer is about **8x**. Runtime slowdown varies greatly +depending on how often the instrumented code relies on type aliasing. In the best case slowdown is +**2x-3x**. + +The compiler instrumentation also has an impact on code size and compilation overhead. There is an +experimental :ref:`instrumentation outlining option<outlining_flag>` which can greatly reduce this +but this may decrease runtime performance. The TypeSanitizer Algorithm =========================== @@ -128,6 +132,23 @@ references to LLVM IR specific terms. Sanitizer features ================== +.. _outlining_flag: + +Instrumentation code outlining +------------------------------ + +By default TypeSanitizer inlines the instrumentation code. This leads to increased +binary size and compilation time. Using the clang flag +``-fsanitize-type-outline-instrumentation`` (default: ``false``) +forces all code instrumentation to be outlined. This reduces the size of the +generated code and reduces compile-time overhead, but it also reduces runtime +performance. + +This outlined instrumentation is new. If you wish to verify that the outlined instrumentation +is behaving in the same way as the inline instrumentation, you can force TypeSanitizer +to use both types of instrumentation. You can use the clang flag +``-fsanitize-type-verify-outlined-instrumentation`` (default: ``false``) to do this. + ``__has_feature(type_sanitizer)`` ------------------------------------ diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index fb22ad3c90af4..04da152bd0aff 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2277,6 +2277,15 @@ are listed below. See :doc: `AddressSanitizer` for more details. +.. option:: -f[no-]sanitize-type-outline-instrumentation + + Controls how type sanitizer code is generated. If enabled will always use + a function call instead of inlining the code. Turning this option on may + reduce the binary size and compilation overhead, but might result in a worse + run-time performance. + + See :doc: `TypeSanitizer` for more details. + .. option:: -f[no-]sanitize-stats Enable simple statistics gathering for the enabled sanitizers. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 6e1c9425d8d75..adef9a592b55e 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2454,6 +2454,18 @@ def fsanitize_address_outline_instrumentation : Flag<["-"], "fsanitize-address-o def fno_sanitize_address_outline_instrumentation : Flag<["-"], "fno-sanitize-address-outline-instrumentation">, Group<f_clang_Group>, HelpText<"Use default code inlining logic for the address sanitizer">; +def fsanitize_type_outline_instrumentation : Flag<["-"], "fsanitize-type-outline-instrumentation">, + Group<f_clang_Group>, + HelpText<"Always generate function calls for type sanitizer instrumentation">; +def fno_sanitize_type_outline_instrumentation : Flag<["-"], "fno-sanitize-type-outline-instrumentation">, + Group<f_clang_Group>, + HelpText<"Use default code inlining logic for the type sanitizer">; +def fsanitize_type_verify_outlined_instrumentation : Flag<["-"], "fsanitize_type_verify_outlined_instrumentation">, + Group<f_clang_Group>, + HelpText<"Use both inlined and outlined instrumentation for type sanitizer to verify equivilence">; +def fno_sanitize_type_verify_outlined_instrumentation : Flag<["-"], "fno_sanitize_type_verify_outlined_instrumentation">, + Group<f_clang_Group>, + HelpText<"Don't use both inlined and outlined instrumentation for type sanitizer to verify equivilence">; defm sanitize_stable_abi : OptInCC1FFlag<"sanitize-stable-abi", "Stable ", "Conventional ", "ABI instrumentation for sanitizer runtime. Default: Conventional">; diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index eea7897e96afd..29569bcf7fa25 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -67,6 +67,8 @@ class SanitizerArgs { bool TsanFuncEntryExit = true; bool TsanAtomics = true; bool MinimalRuntime = false; + bool TysanOutlineInstrumentation = false; + bool TysanVerifyOutlinedInstrumentation = false; // True if cross-dso CFI support if provided by the system (i.e. Android). bool ImplicitCfiRuntime = false; bool NeedsMemProfRt = false; diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 5dd48f53b9069..c106dd63c2805 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -1176,6 +1176,17 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, options::OPT_fno_sanitize_alloc_token_extended, AllocTokenExtended); } + if (AllAddedKinds & SanitizerKind::Type) { + TysanOutlineInstrumentation = + Args.hasFlag(options::OPT_fsanitize_type_outline_instrumentation, + options::OPT_fno_sanitize_type_outline_instrumentation, + TysanOutlineInstrumentation); + TysanVerifyOutlinedInstrumentation = Args.hasFlag( + options::OPT_fsanitize_type_verify_outlined_instrumentation, + options::OPT_fno_sanitize_type_verify_outlined_instrumentation, + TysanVerifyOutlinedInstrumentation); + } + LinkRuntimes = Args.hasFlag(options::OPT_fsanitize_link_runtime, options::OPT_fno_sanitize_link_runtime, !Args.hasArg(options::OPT_r)); @@ -1500,6 +1511,15 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, CmdArgs.push_back("-asan-instrumentation-with-call-threshold=0"); } + if (TysanOutlineInstrumentation || TysanVerifyOutlinedInstrumentation) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-tysan-outline-instrumentation"); + } + if (TysanVerifyOutlinedInstrumentation) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-tysan-verify-outlined-instrumentation"); + } + // When emitting Stable ABI instrumentation, force outlining calls and avoid // inlining shadow memory poisoning. While this is a big performance burden // for now it allows full abstraction from implementation details. diff --git a/clang/test/CodeGen/sanitize-type-outlined.cpp b/clang/test/CodeGen/sanitize-type-outlined.cpp new file mode 100644 index 0000000000000..3c22f15c93d94 --- /dev/null +++ b/clang/test/CodeGen/sanitize-type-outlined.cpp @@ -0,0 +1,23 @@ +// RUN: %clang -S -fsanitize=type -emit-llvm -o - -fsanitize=type %s \ +// RUN: | FileCheck %s --check-prefixes=CHECK-NO-OUTLINE +// RUN: %clang -S -fsanitize=type -emit-llvm -o - -fsanitize=type %s \ +// RUN: -fsanitize-type-outline-instrumentation \ +// RUN: | FileCheck %s --check-prefixes=CHECK-OUTLINE + +// RUN: %clang -S -fsanitize=type -emit-llvm -o - -fsanitize=type %s \ +// RUN: -fsanitize-type-outline-instrumentation \ +// RUN: -fsanitize-type-verify-outlined-instrumentation \ +// RUN: | FileCheck %s --check-prefixes=CHECK-OUTLINE-VERIFY +// RUN: %clang -S -fsanitize=type -emit-llvm -o - -fsanitize=type %s \ +// RUN: -fsanitize-type-verify-outlined-instrumentation \ +// RUN: | FileCheck %s --check-prefixes=CHECK-VERIFY + +// CHECK-NO-OUTLINE-NOT: call{{.*}}@__tysan_instrument_mem_inst +// CHECK-OUTLINE: call{{.*}}@__tysan_instrument_mem_inst +// CHECK-OUTLINE-VERIFY: call{{.*}}@__tysan_instrument_mem_inst +// CHECK-VERIFY: call{{.*}}@__tysan_instrument_mem_inst + +float alias(int *ptr){ + float *aliasedPtr = (float *)ptr; + return *aliasedPtr; +} 
@gbMattN gbMattN added the compiler-rt:tysan Type sanitizer label Nov 3, 2025
HelpText<"Use default code inlining logic for the type sanitizer">;
def fsanitize_type_verify_outlined_instrumentation : Flag<["-"], "fsanitize-type-verify-outlined-instrumentation">,
Group<f_clang_Group>,
HelpText<"Use both inlined and outlined instrumentation for type sanitizer to verify equivilence">;
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: "equivilence"

HelpText<"Use both inlined and outlined instrumentation for type sanitizer to verify equivilence">;
def fno_sanitize_type_verify_outlined_instrumentation : Flag<["-"], "fno_sanitize-type-verify-outlined-instrumentation">,
Group<f_clang_Group>,
HelpText<"Don't use both inlined and outlined instrumentation for type sanitizer to verify equivilence">;
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: "equivilence"

// CHECK-OUTLINE-VERIFY: call{{.*}}@__tysan_instrument_mem_inst
// CHECK-VERIFY: call{{.*}}@__tysan_instrument_mem_inst

float alias(int *ptr){
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use CHECK-LABEL to avoid accidentally passing the test for some reason?

// RUN: -fsanitize-type-verify-outlined-instrumentation \
// RUN: | FileCheck %s --check-prefixes=CHECK-VERIFY

// CHECK-NO-OUTLINE-NOT: call{{.*}}@__tysan_instrument_mem_inst
Copy link
Contributor

Choose a reason for hiding this comment

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

The existing IR test for outline instrumentation (llvm/test/Instrumentation/TypeSanitizer/basic_outlined.ll) checks __tysan_instrument_with_shadow_update.

Why does this test pass? Are there any memintrinsics here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Both __tysan_instrument_mem_inst and __tysan_instrument_with_shadow_update are functions which can be inserted when outlining is enabled, so either (perhaps better if both) can be used to check that the outlining of instrumentation is occurring. I made this test mimicking the minimal one ASan has but on second thought maybe I should make it a bit more verbose to properly check for all possible outlined functions

Copy link
Contributor

Choose a reason for hiding this comment

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

Right. Note the ASan tests were written ages ago, so maybe we know better now :-)

Copy link
Contributor

@fhahn fhahn left a comment

Choose a reason for hiding this comment

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

@gbMattN did you already get a chance to verify the outlined instrumentation matches via -fsanitize-type-verify-outlined-instrumentation on a larger set of workloads?

Are you aware of any cases where the outlined insturmentation performs worse than the inline instrumentation?

If not, I think it would be better to due some due diligence testing using -fsanitize-type-verify-outlined-instrumentation and flip the default, instead of adding the plumbing to make it convenient to use

@gbMattN
Copy link
Contributor Author

gbMattN commented Nov 4, 2025

I'm not aware of any cases where the outlined instrumentation gives you worse diagnostics than the inlined one, but I think before being comfortable changing the default I will want to do a little more testing. What I've done is a little more "ad-hoc" than exhaustive

@fhahn
Copy link
Contributor

fhahn commented Nov 5, 2025

I'm not aware of any cases where the outlined instrumentation gives you worse diagnostics than the inlined one, but I think before being comfortable changing the default I will want to do a little more testing. What I've done is a little more "ad-hoc" than exhaustive

Sounds good, I think it would probably be worth doing that and then flip the default, instead of adding more plumbing, unless the results are not that clear cut

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category compiler-rt:tysan Type sanitizer

4 participants