Skip to content

Commit

Permalink
[IR] Introduce llvm.allow.{runtime,ubsan}.check() (#84850)
Browse files Browse the repository at this point in the history
The goal is to have ability to change logic compile time based on PGO
data.

Our primary application is removing UBSAN checks from hot code.
Then we'd like to use this for libc++ hardening and regular debug
asserts.
Previous attempt is #84214.

Benefits from special intrinsic vs #84214:
1. Resulting binary is 3% faster than removing traps (on
"test-suite/MultiSource/Benchmarks" with PGO+ThinLTO)
2. Intrinsic can be used from source code to change behavior from C/C++
program. E.g. enabling asserts in cold code.
3. Easier to match basic blocks.

RFC:
https://discourse.llvm.org/t/rfc-add-llvm-experimental-hot-intrinsic-or-llvm-hot/77641

---------

Co-authored-by: Nikita Popov <npopov@redhat.com>
  • Loading branch information
vitalybuka and nikic committed Apr 1, 2024
1 parent 70deb7b commit 90c738e
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 0 deletions.
114 changes: 114 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27839,6 +27839,120 @@ constant `true`. However it is always correct to replace
it with any other `i1` value. Any pass can
freely do it if it can benefit from non-default lowering.

'``llvm.allow.ubsan.check``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

::

declare i1 @llvm.allow.ubsan.check(i8 immarg %kind)

Overview:
"""""""""

This intrinsic returns ``true`` if and only if the compiler opted to enable the
ubsan check in the current basic block.

Rules to allow ubsan checks are not part of the intrinsic declaration, and
controlled by compiler options.

This intrinsic is the ubsan specific version of ``@llvm.allow.runtime.check()``.

Arguments:
""""""""""

An integer describing the kind of ubsan check guarded by the intrinsic.

Semantics:
""""""""""

The intrinsic ``@llvm.allow.ubsan.check()`` returns either ``true`` or
``false``, depending on compiler options.

For each evaluation of a call to this intrinsic, the program must be valid and
correct both if it returns ``true`` and if it returns ``false``.

When used in a branch condition, it selects one of the two paths:

* `true``: Executes the UBSan check and reports any failures.

* `false`: Bypasses the check, assuming it always succeeds.

Example:

.. code-block:: text

%allow = call i1 @llvm.allow.ubsan.check(i8 5)
%not.allow = xor i1 %allow, true
%cond = or i1 %ubcheck, %not.allow
br i1 %cond, label %cont, label %trap

cont:
; Proceed

trap:
call void @llvm.ubsantrap(i8 5)
unreachable


'``llvm.allow.runtime.check``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax:
"""""""

::

declare i1 @llvm.allow.runtime.check(metadata %kind)

Overview:
"""""""""

This intrinsic returns ``true`` if and only if the compiler opted to enable
runtime checks in the current basic block.

Rules to allow runtime checks are not part of the intrinsic declaration, and
controlled by compiler options.

This intrinsic is non-ubsan specific version of ``@llvm.allow.ubsan.check()``.

Arguments:
""""""""""

A string identifying the kind of runtime check guarded by the intrinsic. The
string can be used to control rules to allow checks.

Semantics:
""""""""""

The intrinsic ``@llvm.allow.runtime.check()`` returns either ``true`` or
``false``, depending on compiler options.

For each evaluation of a call to this intrinsic, the program must be valid and
correct both if it returns ``true`` and if it returns ``false``.

When used in a branch condition, it allows us to choose between
two alternative correct solutions for the same problem.

If the intrinsic is evaluated as ``true``, program should execute a guarded
check. If the intrinsic is evaluated as ``false``, the program should avoid any
unnecessary checks.

Example:

.. code-block:: text

%allow = call i1 @llvm.allow.runtime.check(metadata !"my_check")
br i1 %allow, label %fast_path, label %slow_path

fast_path:
; Omit diagnostics.

slow_path:
; Additional diagnostics.


'``llvm.load.relative``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
10 changes: 10 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Original file line number Diff line number Diff line change
Expand Up @@ -1731,6 +1731,16 @@ def int_debugtrap : Intrinsic<[]>,
def int_ubsantrap : Intrinsic<[], [llvm_i8_ty],
[IntrNoReturn, IntrCold, ImmArg<ArgIndex<0>>]>;

// Return true if ubsan check is allowed.
def int_allow_ubsan_check : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_i8_ty],
[IntrInaccessibleMemOnly, IntrWriteMem, ImmArg<ArgIndex<0>>, NoUndef<RetIndex>]>,
ClangBuiltin<"__builtin_allow_ubsan_check">;

// Return true if runtime check is allowed.
def int_allow_runtime_check : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_metadata_ty],
[IntrInaccessibleMemOnly, IntrWriteMem, NoUndef<RetIndex>]>,
ClangBuiltin<"__builtin_allow_runtime_check">;

// Support for dynamic deoptimization (or de-specialization)
def int_experimental_deoptimize : Intrinsic<[llvm_any_ty], [llvm_vararg_ty],
[Throws]>;
Expand Down

0 comments on commit 90c738e

Please sign in to comment.