Files
clang-p2996/llvm/test/Transforms/Inline/attributes.ll
Nick Desaulniers 2240d72f15 [X86] initial -mfunction-return=thunk-extern support
Adds support for:
* `-mfunction-return=<value>` command line flag, and
* `__attribute__((function_return("<value>")))` function attribute

Where the supported <value>s are:
* keep (disable)
* thunk-extern (enable)

thunk-extern enables clang to change ret instructions into jmps to an
external symbol named __x86_return_thunk, implemented as a new
MachineFunctionPass named "x86-return-thunks", keyed off the new IR
attribute fn_ret_thunk_extern.

The symbol __x86_return_thunk is expected to be provided by the runtime
the compiled code is linked against and is not defined by the compiler.
Enabling this option alone doesn't provide mitigations without
corresponding definitions of __x86_return_thunk!

This new MachineFunctionPass is very similar to "x86-lvi-ret".

The <value>s "thunk" and "thunk-inline" are currently unsupported. It's
not clear yet that they are necessary: whether the thunk pattern they
would emit is beneficial or used anywhere.

Should the <value>s "thunk" and "thunk-inline" become necessary,
x86-return-thunks could probably be merged into x86-retpoline-thunks
which has pre-existing machinery for emitting thunks (which could be
used to implement the <value> "thunk").

Has been found to build+boot with corresponding Linux
kernel patches. This helps the Linux kernel mitigate RETBLEED.
* CVE-2022-23816
* CVE-2022-28693
* CVE-2022-29901

See also:
* "RETBLEED: Arbitrary Speculative Code Execution with Return
Instructions."
* AMD SECURITY NOTICE AMD-SN-1037: AMD CPU Branch Type Confusion
* TECHNICAL GUIDANCE FOR MITIGATING BRANCH TYPE CONFUSION REVISION 1.0
  2022-07-12
* Return Stack Buffer Underflow / Return Stack Buffer Underflow /
  CVE-2022-29901, CVE-2022-28693 / INTEL-SA-00702

SystemZ may eventually want to support "thunk-extern" and "thunk"; both
options are used by the Linux kernel's CONFIG_EXPOLINE.

This functionality has been available in GCC since the 8.1 release, and
was backported to the 7.3 release.

Many thanks for folks that provided discrete review off list due to the
embargoed nature of this hardware vulnerability. Many Bothans died to
bring us this information.

Link: https://www.youtube.com/watch?v=IF6HbCKQHK8
Link: https://github.com/llvm/llvm-project/issues/54404
Link: https://gcc.gnu.org/legacy-ml/gcc-patches/2018-01/msg01197.html
Link: https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/return-stack-buffer-underflow.html
Link: https://arstechnica.com/information-technology/2022/07/intel-and-amd-cpus-vulnerable-to-a-new-speculative-execution-attack/?comments=1
Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ce114c866860aa9eae3f50974efc68241186ba60
Link: https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00702.html
Link: https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00707.html

Reviewed By: aaron.ballman, craig.topper

Differential Revision: https://reviews.llvm.org/D129572
2022-07-12 09:17:54 -07:00

645 lines
20 KiB
LLVM

; RUN: opt < %s -inline -S | FileCheck %s
; RUN: opt < %s -passes='cgscc(inline)' -S | FileCheck %s
target datalayout = "E-p:64:64:64-a0:0:8-f32:32:32-f64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-v64:64:64-v128:128:128"
define i32 @noattr_callee(i32 %i) {
ret i32 %i
}
define i32 @sanitize_address_callee(i32 %i) sanitize_address {
ret i32 %i
}
define i32 @sanitize_hwaddress_callee(i32 %i) sanitize_hwaddress {
ret i32 %i
}
define i32 @sanitize_thread_callee(i32 %i) sanitize_thread {
ret i32 %i
}
define i32 @sanitize_memory_callee(i32 %i) sanitize_memory {
ret i32 %i
}
define i32 @sanitize_memtag_callee(i32 %i) sanitize_memtag {
ret i32 %i
}
define i32 @safestack_callee(i32 %i) safestack {
ret i32 %i
}
define i32 @slh_callee(i32 %i) speculative_load_hardening {
ret i32 %i
}
define i32 @alwaysinline_callee(i32 %i) alwaysinline {
ret i32 %i
}
define i32 @alwaysinline_sanitize_address_callee(i32 %i) alwaysinline sanitize_address {
ret i32 %i
}
define i32 @alwaysinline_sanitize_hwaddress_callee(i32 %i) alwaysinline sanitize_hwaddress {
ret i32 %i
}
define i32 @alwaysinline_sanitize_thread_callee(i32 %i) alwaysinline sanitize_thread {
ret i32 %i
}
define i32 @alwaysinline_sanitize_memory_callee(i32 %i) alwaysinline sanitize_memory {
ret i32 %i
}
define i32 @alwaysinline_sanitize_memtag_callee(i32 %i) alwaysinline sanitize_memtag {
ret i32 %i
}
define i32 @alwaysinline_safestack_callee(i32 %i) alwaysinline safestack {
ret i32 %i
}
; Check that:
; * noattr callee is inlined into noattr caller,
; * sanitize_(address|memory|thread) callee is not inlined into noattr caller,
; * alwaysinline callee is always inlined no matter what sanitize_* attributes are present.
define i32 @test_no_sanitize_address(i32 %arg) {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_address_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_address_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_no_sanitize_address(
; CHECK-NEXT: @sanitize_address_callee
; CHECK-NEXT: ret i32
}
define i32 @test_no_sanitize_hwaddress(i32 %arg) {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_hwaddress_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_hwaddress_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_no_sanitize_hwaddress(
; CHECK-NEXT: @sanitize_hwaddress_callee
; CHECK-NEXT: ret i32
}
define i32 @test_no_sanitize_memory(i32 %arg) {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_memory_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_memory_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_no_sanitize_memory(
; CHECK-NEXT: @sanitize_memory_callee
; CHECK-NEXT: ret i32
}
define i32 @test_no_sanitize_thread(i32 %arg) {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_thread_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_thread_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_no_sanitize_thread(
; CHECK-NEXT: @sanitize_thread_callee
; CHECK-NEXT: ret i32
}
define i32 @test_no_sanitize_memtag(i32 %arg) {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_memtag_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_memtag_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_no_sanitize_memtag(
; CHECK-NEXT: @sanitize_memtag_callee
; CHECK-NEXT: ret i32
}
; Check that:
; * noattr callee is not inlined into sanitize_(address|memory|thread) caller,
; * sanitize_(address|memory|thread) callee is inlined into the caller with the same attribute,
; * alwaysinline callee is always inlined no matter what sanitize_* attributes are present.
define i32 @test_sanitize_address(i32 %arg) sanitize_address {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_address_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_address_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_sanitize_address(
; CHECK-NEXT: @noattr_callee
; CHECK-NEXT: ret i32
}
define i32 @test_sanitize_hwaddress(i32 %arg) sanitize_hwaddress {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_hwaddress_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_hwaddress_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_sanitize_hwaddress(
; CHECK-NEXT: @noattr_callee
; CHECK-NEXT: ret i32
}
define i32 @test_sanitize_memory(i32 %arg) sanitize_memory {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_memory_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_memory_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_sanitize_memory(
; CHECK-NEXT: @noattr_callee
; CHECK-NEXT: ret i32
}
define i32 @test_sanitize_thread(i32 %arg) sanitize_thread {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_thread_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_thread_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_sanitize_thread(
; CHECK-NEXT: @noattr_callee
; CHECK-NEXT: ret i32
}
define i32 @test_sanitize_memtag(i32 %arg) sanitize_memtag {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @sanitize_memtag_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_sanitize_memtag_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_sanitize_memtag(
; CHECK-NEXT: @noattr_callee
; CHECK-NEXT: ret i32
}
define i32 @test_safestack(i32 %arg) safestack {
%x1 = call i32 @noattr_callee(i32 %arg)
%x2 = call i32 @safestack_callee(i32 %x1)
%x3 = call i32 @alwaysinline_callee(i32 %x2)
%x4 = call i32 @alwaysinline_safestack_callee(i32 %x3)
ret i32 %x4
; CHECK-LABEL: @test_safestack(
; CHECK-NEXT: @noattr_callee
; CHECK-NEXT: ret i32
}
; Can inline a normal function into an SLH'ed function.
define i32 @test_caller_slh(i32 %i) speculative_load_hardening {
; CHECK-LABEL: @test_caller_slh(
; CHECK-SAME: ) [[SLH:.*]] {
; CHECK-NOT: call
; CHECK: ret i32
entry:
%callee = call i32 @noattr_callee(i32 %i)
ret i32 %callee
}
; Can inline a SLH'ed function into a normal one, propagating SLH.
define i32 @test_callee_slh(i32 %i) {
; CHECK-LABEL: @test_callee_slh(
; CHECK-SAME: ) [[SLH:.*]] {
; CHECK-NOT: call
; CHECK: ret i32
entry:
%callee = call i32 @slh_callee(i32 %i)
ret i32 %callee
}
; Check that a function doesn't get inlined if target-cpu strings don't match
; exactly.
define i32 @test_target_cpu_callee0(i32 %i) "target-cpu"="corei7" {
ret i32 %i
}
define i32 @test_target_cpu0(i32 %i) "target-cpu"="corei7" {
%1 = call i32 @test_target_cpu_callee0(i32 %i)
ret i32 %1
; CHECK-LABEL: @test_target_cpu0(
; CHECK-NOT: @test_target_cpu_callee0
}
define i32 @test_target_cpu_callee1(i32 %i) "target-cpu"="x86-64" {
ret i32 %i
}
define i32 @test_target_cpu1(i32 %i) "target-cpu"="corei7" {
%1 = call i32 @test_target_cpu_callee1(i32 %i)
ret i32 %1
; CHECK-LABEL: @test_target_cpu1(
; CHECK-NEXT: @test_target_cpu_callee1
; CHECK-NEXT: ret i32
}
; Check that a function doesn't get inlined if target-features strings don't
; match exactly.
define i32 @test_target_features_callee0(i32 %i) "target-features"="+sse4.2" {
ret i32 %i
}
define i32 @test_target_features0(i32 %i) "target-features"="+sse4.2" {
%1 = call i32 @test_target_features_callee0(i32 %i)
ret i32 %1
; CHECK-LABEL: @test_target_features0(
; CHECK-NOT: @test_target_features_callee0
}
define i32 @test_target_features_callee1(i32 %i) "target-features"="+avx2" {
ret i32 %i
}
define i32 @test_target_features1(i32 %i) "target-features"="+sse4.2" {
%1 = call i32 @test_target_features_callee1(i32 %i)
ret i32 %1
; CHECK-LABEL: @test_target_features1(
; CHECK-NEXT: @test_target_features_callee1
; CHECK-NEXT: ret i32
}
define i32 @less-precise-fpmad_callee0(i32 %i) "less-precise-fpmad"="false" {
ret i32 %i
; CHECK: @less-precise-fpmad_callee0(i32 %i) [[FPMAD_FALSE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @less-precise-fpmad_callee1(i32 %i) "less-precise-fpmad"="true" {
ret i32 %i
; CHECK: @less-precise-fpmad_callee1(i32 %i) [[FPMAD_TRUE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @test_less-precise-fpmad0(i32 %i) "less-precise-fpmad"="false" {
%1 = call i32 @less-precise-fpmad_callee0(i32 %i)
ret i32 %1
; CHECK: @test_less-precise-fpmad0(i32 %i) [[FPMAD_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_less-precise-fpmad1(i32 %i) "less-precise-fpmad"="false" {
%1 = call i32 @less-precise-fpmad_callee1(i32 %i)
ret i32 %1
; CHECK: @test_less-precise-fpmad1(i32 %i) [[FPMAD_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_less-precise-fpmad2(i32 %i) "less-precise-fpmad"="true" {
%1 = call i32 @less-precise-fpmad_callee0(i32 %i)
ret i32 %1
; CHECK: @test_less-precise-fpmad2(i32 %i) [[FPMAD_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_less-precise-fpmad3(i32 %i) "less-precise-fpmad"="true" {
%1 = call i32 @less-precise-fpmad_callee1(i32 %i)
ret i32 %1
; CHECK: @test_less-precise-fpmad3(i32 %i) [[FPMAD_TRUE]] {
; CHECK-NEXT: ret i32
}
define i32 @no-implicit-float_callee0(i32 %i) {
ret i32 %i
; CHECK: @no-implicit-float_callee0(i32 %i) {
; CHECK-NEXT: ret i32
}
define i32 @no-implicit-float_callee1(i32 %i) noimplicitfloat {
ret i32 %i
; CHECK: @no-implicit-float_callee1(i32 %i) [[NOIMPLICITFLOAT:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-implicit-float0(i32 %i) {
%1 = call i32 @no-implicit-float_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-implicit-float0(i32 %i) {
; CHECK-NEXT: ret i32
}
define i32 @test_no-implicit-float1(i32 %i) {
%1 = call i32 @no-implicit-float_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-implicit-float1(i32 %i) [[NOIMPLICITFLOAT]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-implicit-float2(i32 %i) noimplicitfloat {
%1 = call i32 @no-implicit-float_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-implicit-float2(i32 %i) [[NOIMPLICITFLOAT]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-implicit-float3(i32 %i) noimplicitfloat {
%1 = call i32 @no-implicit-float_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-implicit-float3(i32 %i) [[NOIMPLICITFLOAT]] {
; CHECK-NEXT: ret i32
}
; Check that no-jump-tables flag propagates from inlined callee to caller
define i32 @no-use-jump-tables_callee0(i32 %i) {
ret i32 %i
; CHECK: @no-use-jump-tables_callee0(i32 %i) {
; CHECK-NEXT: ret i32
}
define i32 @no-use-jump-tables_callee1(i32 %i) "no-jump-tables"="true" {
ret i32 %i
; CHECK: @no-use-jump-tables_callee1(i32 %i) [[NOUSEJUMPTABLES:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-use-jump-tables0(i32 %i) {
%1 = call i32 @no-use-jump-tables_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-use-jump-tables0(i32 %i) {
; CHECK-NEXT: ret i32
}
define i32 @test_no-use-jump-tables1(i32 %i) {
%1 = call i32 @no-use-jump-tables_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-use-jump-tables1(i32 %i) [[NOUSEJUMPTABLES]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-use-jump-tables2(i32 %i) "no-jump-tables"="true" {
%1 = call i32 @no-use-jump-tables_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-use-jump-tables2(i32 %i) [[NOUSEJUMPTABLES]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-use-jump-tables3(i32 %i) "no-jump-tables"="true" {
%1 = call i32 @no-use-jump-tables_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-use-jump-tables3(i32 %i) [[NOUSEJUMPTABLES]] {
; CHECK-NEXT: ret i32
}
; Callee with null_pointer_is_valid attribute should not be inlined
; into a caller without this attribute.
; Exception: alwaysinline callee can still be inlined but
; null_pointer_is_valid should get copied to caller.
define i32 @null-pointer-is-valid_callee0(i32 %i) null_pointer_is_valid {
ret i32 %i
; CHECK: @null-pointer-is-valid_callee0(i32 %i)
; CHECK-NEXT: ret i32
}
define i32 @null-pointer-is-valid_callee1(i32 %i) alwaysinline null_pointer_is_valid {
ret i32 %i
; CHECK: @null-pointer-is-valid_callee1(i32 %i)
; CHECK-NEXT: ret i32
}
define i32 @null-pointer-is-valid_callee2(i32 %i) {
ret i32 %i
; CHECK: @null-pointer-is-valid_callee2(i32 %i)
; CHECK-NEXT: ret i32
}
; No inlining since caller does not have null_pointer_is_valid attribute.
define i32 @test_null-pointer-is-valid0(i32 %i) {
%1 = call i32 @null-pointer-is-valid_callee0(i32 %i)
ret i32 %1
; CHECK: @test_null-pointer-is-valid0(
; CHECK: call i32 @null-pointer-is-valid_callee0
; CHECK-NEXT: ret i32
}
; alwaysinline should force inlining even when caller does not have
; null_pointer_is_valid attribute. However, the attribute should be
; copied to caller.
define i32 @test_null-pointer-is-valid1(i32 %i) {
%1 = call i32 @null-pointer-is-valid_callee1(i32 %i)
ret i32 %1
; CHECK: @test_null-pointer-is-valid1(i32 %i) [[NULLPOINTERISVALID:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
; Can inline since both caller and callee have null_pointer_is_valid
; attribute.
define i32 @test_null-pointer-is-valid2(i32 %i) null_pointer_is_valid {
%1 = call i32 @null-pointer-is-valid_callee2(i32 %i)
ret i32 %1
; CHECK: @test_null-pointer-is-valid2(i32 %i) [[NULLPOINTERISVALID]] {
; CHECK-NEXT: ret i32
}
define i32 @no-infs-fp-math_callee0(i32 %i) "no-infs-fp-math"="false" {
ret i32 %i
; CHECK: @no-infs-fp-math_callee0(i32 %i) [[NO_INFS_FPMATH_FALSE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @no-infs-fp-math_callee1(i32 %i) "no-infs-fp-math"="true" {
ret i32 %i
; CHECK: @no-infs-fp-math_callee1(i32 %i) [[NO_INFS_FPMATH_TRUE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-infs-fp-math0(i32 %i) "no-infs-fp-math"="false" {
%1 = call i32 @no-infs-fp-math_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-infs-fp-math0(i32 %i) [[NO_INFS_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-infs-fp-math1(i32 %i) "no-infs-fp-math"="false" {
%1 = call i32 @no-infs-fp-math_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-infs-fp-math1(i32 %i) [[NO_INFS_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-infs-fp-math2(i32 %i) "no-infs-fp-math"="true" {
%1 = call i32 @no-infs-fp-math_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-infs-fp-math2(i32 %i) [[NO_INFS_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-infs-fp-math3(i32 %i) "no-infs-fp-math"="true" {
%1 = call i32 @no-infs-fp-math_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-infs-fp-math3(i32 %i) [[NO_INFS_FPMATH_TRUE]] {
; CHECK-NEXT: ret i32
}
define i32 @no-nans-fp-math_callee0(i32 %i) "no-nans-fp-math"="false" {
ret i32 %i
; CHECK: @no-nans-fp-math_callee0(i32 %i) [[NO_NANS_FPMATH_FALSE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @no-nans-fp-math_callee1(i32 %i) "no-nans-fp-math"="true" {
ret i32 %i
; CHECK: @no-nans-fp-math_callee1(i32 %i) [[NO_NANS_FPMATH_TRUE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-nans-fp-math0(i32 %i) "no-nans-fp-math"="false" {
%1 = call i32 @no-nans-fp-math_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-nans-fp-math0(i32 %i) [[NO_NANS_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-nans-fp-math1(i32 %i) "no-nans-fp-math"="false" {
%1 = call i32 @no-nans-fp-math_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-nans-fp-math1(i32 %i) [[NO_NANS_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-nans-fp-math2(i32 %i) "no-nans-fp-math"="true" {
%1 = call i32 @no-nans-fp-math_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-nans-fp-math2(i32 %i) [[NO_NANS_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-nans-fp-math3(i32 %i) "no-nans-fp-math"="true" {
%1 = call i32 @no-nans-fp-math_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-nans-fp-math3(i32 %i) [[NO_NANS_FPMATH_TRUE]] {
; CHECK-NEXT: ret i32
}
define i32 @no-signed-zeros-fp-math_callee0(i32 %i) "no-signed-zeros-fp-math"="false" {
ret i32 %i
; CHECK: @no-signed-zeros-fp-math_callee0(i32 %i) [[NO_SIGNED_ZEROS_FPMATH_FALSE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @no-signed-zeros-fp-math_callee1(i32 %i) "no-signed-zeros-fp-math"="true" {
ret i32 %i
; CHECK: @no-signed-zeros-fp-math_callee1(i32 %i) [[NO_SIGNED_ZEROS_FPMATH_TRUE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-signed-zeros-fp-math0(i32 %i) "no-signed-zeros-fp-math"="false" {
%1 = call i32 @no-signed-zeros-fp-math_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-signed-zeros-fp-math0(i32 %i) [[NO_SIGNED_ZEROS_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-signed-zeros-fp-math1(i32 %i) "no-signed-zeros-fp-math"="false" {
%1 = call i32 @no-signed-zeros-fp-math_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-signed-zeros-fp-math1(i32 %i) [[NO_SIGNED_ZEROS_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-signed-zeros-fp-math2(i32 %i) "no-signed-zeros-fp-math"="true" {
%1 = call i32 @no-signed-zeros-fp-math_callee0(i32 %i)
ret i32 %1
; CHECK: @test_no-signed-zeros-fp-math2(i32 %i) [[NO_SIGNED_ZEROS_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_no-signed-zeros-fp-math3(i32 %i) "no-signed-zeros-fp-math"="true" {
%1 = call i32 @no-signed-zeros-fp-math_callee1(i32 %i)
ret i32 %1
; CHECK: @test_no-signed-zeros-fp-math3(i32 %i) [[NO_SIGNED_ZEROS_FPMATH_TRUE]] {
; CHECK-NEXT: ret i32
}
define i32 @unsafe-fp-math_callee0(i32 %i) "unsafe-fp-math"="false" {
ret i32 %i
; CHECK: @unsafe-fp-math_callee0(i32 %i) [[UNSAFE_FPMATH_FALSE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @unsafe-fp-math_callee1(i32 %i) "unsafe-fp-math"="true" {
ret i32 %i
; CHECK: @unsafe-fp-math_callee1(i32 %i) [[UNSAFE_FPMATH_TRUE:#[0-9]+]] {
; CHECK-NEXT: ret i32
}
define i32 @test_unsafe-fp-math0(i32 %i) "unsafe-fp-math"="false" {
%1 = call i32 @unsafe-fp-math_callee0(i32 %i)
ret i32 %1
; CHECK: @test_unsafe-fp-math0(i32 %i) [[UNSAFE_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_unsafe-fp-math1(i32 %i) "unsafe-fp-math"="false" {
%1 = call i32 @unsafe-fp-math_callee1(i32 %i)
ret i32 %1
; CHECK: @test_unsafe-fp-math1(i32 %i) [[UNSAFE_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_unsafe-fp-math2(i32 %i) "unsafe-fp-math"="true" {
%1 = call i32 @unsafe-fp-math_callee0(i32 %i)
ret i32 %1
; CHECK: @test_unsafe-fp-math2(i32 %i) [[UNSAFE_FPMATH_FALSE]] {
; CHECK-NEXT: ret i32
}
define i32 @test_unsafe-fp-math3(i32 %i) "unsafe-fp-math"="true" {
%1 = call i32 @unsafe-fp-math_callee1(i32 %i)
ret i32 %1
; CHECK: @test_unsafe-fp-math3(i32 %i) [[UNSAFE_FPMATH_TRUE]] {
; CHECK-NEXT: ret i32
}
; Test that fn_ret_thunk_extern has no CompatRule; inlining is permitted.
; Test that fn_ret_thunk_extern has no MergeRule; fn_ret_thunk_extern is not
; propagated or dropped on the caller after inlining.
define i32 @thunk_extern_callee() fn_ret_thunk_extern {
; CHECK: @thunk_extern_callee() [[FNRETTHUNK_EXTERN:#[0-9]+]]
ret i32 42
}
define i32 @thunk_keep_caller() {
; CHECK: @thunk_keep_caller() {
; CHECK-NEXT: ret i32 42
%1 = call i32 @thunk_extern_callee()
ret i32 %1
}
define i32 @thunk_keep_callee() {
; CHECK: @thunk_keep_callee() {
ret i32 42
}
define i32 @thunk_extern_caller() fn_ret_thunk_extern {
; CHECK: @thunk_extern_caller() [[FNRETTHUNK_EXTERN]]
; CHECK-NEXT: ret i32 42
%1 = call i32 @thunk_keep_callee()
ret i32 %1
}
; CHECK: attributes [[SLH]] = { speculative_load_hardening }
; CHECK: attributes [[FPMAD_FALSE]] = { "less-precise-fpmad"="false" }
; CHECK: attributes [[FPMAD_TRUE]] = { "less-precise-fpmad"="true" }
; CHECK: attributes [[NOIMPLICITFLOAT]] = { noimplicitfloat }
; CHECK: attributes [[NOUSEJUMPTABLES]] = { "no-jump-tables"="true" }
; CHECK: attributes [[NULLPOINTERISVALID]] = { null_pointer_is_valid }
; CHECK: attributes [[NO_INFS_FPMATH_FALSE]] = { "no-infs-fp-math"="false" }
; CHECK: attributes [[NO_INFS_FPMATH_TRUE]] = { "no-infs-fp-math"="true" }
; CHECK: attributes [[NO_NANS_FPMATH_FALSE]] = { "no-nans-fp-math"="false" }
; CHECK: attributes [[NO_NANS_FPMATH_TRUE]] = { "no-nans-fp-math"="true" }
; CHECK: attributes [[NO_SIGNED_ZEROS_FPMATH_FALSE]] = { "no-signed-zeros-fp-math"="false" }
; CHECK: attributes [[NO_SIGNED_ZEROS_FPMATH_TRUE]] = { "no-signed-zeros-fp-math"="true" }
; CHECK: attributes [[UNSAFE_FPMATH_FALSE]] = { "unsafe-fp-math"="false" }
; CHECK: attributes [[UNSAFE_FPMATH_TRUE]] = { "unsafe-fp-math"="true" }
; CHECK: attributes [[FNRETTHUNK_EXTERN]] = { fn_ret_thunk_extern }