llvm-project/llvm/test/Transforms/InstCombine/ptrauth-intrinsics-call.ll
Ahmed Bougacha 77bcab835a
[InstCombine] Combine ptrauth intrin. callee into same-key bundle. (#94707)
Try to optimize a call to the result of a ptrauth intrinsic, potentially
into the ptrauth call bundle:
    call(ptrauth.resign(p)), ["ptrauth"()] ->  call p, ["ptrauth"()]
    call(ptrauth.sign(p)),   ["ptrauth"()] ->  call p

as long as the key/discriminator are the same in sign and auth-bundle,
and we don't change the key in the bundle (to a potentially-invalid
key.)

Generating a plain call to a raw unauthenticated pointer is generally
undesirable, but if we ended up seeing a naked ptrauth.sign in the first
place, we already have suspicious code. Unauthenticated calls are also
easier to spot than naked signs, so let the indirect call shine.


Note that there is an arguably unsafe extension to this, where we don't
bother checking that the key in bundle and intrinsic are the same (and
also allow folding away an auth into a bundle.)

This can end up generating calls with a bundle that has an invalid key
(which an informed frontend wouldn't have otherwise done), which can be
problematic. The C that generates that is straightforward but arguably
unreasonable. That wouldn't be an issue if we were to bite the bullet
and make these fully AArch64-specific, allowing key knowledge to be
embedded here.
2025-07-15 14:39:53 -07:00

143 lines
5.8 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -passes=instcombine -S | FileCheck %s
define i32 @test_ptrauth_call_sign(ptr %p) {
; CHECK-LABEL: @test_ptrauth_call_sign(
; CHECK-NEXT: [[V3:%.*]] = call i32 [[P:%.*]]()
; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = ptrtoint ptr %p to i64
%v1 = call i64 @llvm.ptrauth.sign(i64 %v0, i32 2, i64 5678)
%v2 = inttoptr i64 %v1 to ptr
%v3 = call i32 %v2() [ "ptrauth"(i32 2, i64 5678) ]
ret i32 %v3
}
define i32 @test_ptrauth_call_sign_otherbundle(ptr %p) {
; CHECK-LABEL: @test_ptrauth_call_sign_otherbundle(
; CHECK-NEXT: [[V3:%.*]] = call i32 [[P:%.*]]() [ "somebundle"(ptr null), "otherbundle"(i64 0) ]
; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = ptrtoint ptr %p to i64
%v1 = call i64 @llvm.ptrauth.sign(i64 %v0, i32 2, i64 5678)
%v2 = inttoptr i64 %v1 to ptr
%v3 = call i32 %v2() [ "somebundle"(ptr null), "ptrauth"(i32 2, i64 5678), "otherbundle"(i64 0) ]
ret i32 %v3
}
define i32 @test_ptrauth_call_resign(ptr %p) {
; CHECK-LABEL: @test_ptrauth_call_resign(
; CHECK-NEXT: [[V3:%.*]] = call i32 [[P:%.*]]() [ "ptrauth"(i32 1, i64 1234) ]
; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = ptrtoint ptr %p to i64
%v1 = call i64 @llvm.ptrauth.resign(i64 %v0, i32 1, i64 1234, i32 1, i64 5678)
%v2 = inttoptr i64 %v1 to ptr
%v3 = call i32 %v2() [ "ptrauth"(i32 1, i64 5678) ]
ret i32 %v3
}
define i32 @test_ptrauth_call_resign_blend(ptr %pp) {
; CHECK-LABEL: @test_ptrauth_call_resign_blend(
; CHECK-NEXT: [[V01:%.*]] = load ptr, ptr [[PP:%.*]], align 8
; CHECK-NEXT: [[V6:%.*]] = call i32 [[V01]]() [ "ptrauth"(i32 1, i64 1234) ]
; CHECK-NEXT: ret i32 [[V6]]
;
%v0 = load ptr, ptr %pp, align 8
%v1 = ptrtoint ptr %pp to i64
%v2 = ptrtoint ptr %v0 to i64
%v3 = call i64 @llvm.ptrauth.blend(i64 %v1, i64 5678)
%v4 = call i64 @llvm.ptrauth.resign(i64 %v2, i32 1, i64 1234, i32 1, i64 %v3)
%v5 = inttoptr i64 %v4 to ptr
%v6 = call i32 %v5() [ "ptrauth"(i32 1, i64 %v3) ]
ret i32 %v6
}
define i32 @test_ptrauth_call_resign_blend_2(ptr %pp) {
; CHECK-LABEL: @test_ptrauth_call_resign_blend_2(
; CHECK-NEXT: [[V01:%.*]] = load ptr, ptr [[PP:%.*]], align 8
; CHECK-NEXT: [[V1:%.*]] = ptrtoint ptr [[PP]] to i64
; CHECK-NEXT: [[V3:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[V1]], i64 5678)
; CHECK-NEXT: [[V6:%.*]] = call i32 [[V01]]() [ "ptrauth"(i32 0, i64 [[V3]]) ]
; CHECK-NEXT: ret i32 [[V6]]
;
%v0 = load ptr, ptr %pp, align 8
%v1 = ptrtoint ptr %pp to i64
%v2 = ptrtoint ptr %v0 to i64
%v3 = call i64 @llvm.ptrauth.blend(i64 %v1, i64 5678)
%v4 = call i64 @llvm.ptrauth.resign(i64 %v2, i32 0, i64 %v3, i32 0, i64 1234)
%v5 = inttoptr i64 %v4 to ptr
%v6 = call i32 %v5() [ "ptrauth"(i32 0, i64 1234) ]
ret i32 %v6
}
define i32 @test_ptrauth_call_resign_mismatch_key(ptr %p) {
; CHECK-LABEL: @test_ptrauth_call_resign_mismatch_key(
; CHECK-NEXT: [[V0:%.*]] = ptrtoint ptr [[P:%.*]] to i64
; CHECK-NEXT: [[V1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[V0]], i32 1, i64 1234, i32 0, i64 5678)
; CHECK-NEXT: [[V2:%.*]] = inttoptr i64 [[V1]] to ptr
; CHECK-NEXT: [[V3:%.*]] = call i32 [[V2]]() [ "ptrauth"(i32 1, i64 5678) ]
; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = ptrtoint ptr %p to i64
%v1 = call i64 @llvm.ptrauth.resign(i64 %v0, i32 1, i64 1234, i32 0, i64 5678)
%v2 = inttoptr i64 %v1 to ptr
%v3 = call i32 %v2() [ "ptrauth"(i32 1, i64 5678) ]
ret i32 %v3
}
define i32 @test_ptrauth_call_resign_mismatch_disc(ptr %p) {
; CHECK-LABEL: @test_ptrauth_call_resign_mismatch_disc(
; CHECK-NEXT: [[V0:%.*]] = ptrtoint ptr [[P:%.*]] to i64
; CHECK-NEXT: [[V1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[V0]], i32 1, i64 1234, i32 0, i64 9900)
; CHECK-NEXT: [[V2:%.*]] = inttoptr i64 [[V1]] to ptr
; CHECK-NEXT: [[V3:%.*]] = call i32 [[V2]]() [ "ptrauth"(i32 1, i64 5678) ]
; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = ptrtoint ptr %p to i64
%v1 = call i64 @llvm.ptrauth.resign(i64 %v0, i32 1, i64 1234, i32 0, i64 9900)
%v2 = inttoptr i64 %v1 to ptr
%v3 = call i32 %v2() [ "ptrauth"(i32 1, i64 5678) ]
ret i32 %v3
}
define i32 @test_ptrauth_call_resign_mismatch_blend(ptr %pp) {
; CHECK-LABEL: @test_ptrauth_call_resign_mismatch_blend(
; CHECK-NEXT: [[V0:%.*]] = load ptr, ptr [[PP:%.*]], align 8
; CHECK-NEXT: [[V1:%.*]] = ptrtoint ptr [[PP]] to i64
; CHECK-NEXT: [[V2:%.*]] = ptrtoint ptr [[V0]] to i64
; CHECK-NEXT: [[V6:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[V1]], i64 5678)
; CHECK-NEXT: [[V4:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[V2]], i32 1, i64 1234, i32 1, i64 [[V6]])
; CHECK-NEXT: [[V5:%.*]] = inttoptr i64 [[V4]] to ptr
; CHECK-NEXT: [[V3:%.*]] = call i32 [[V5]]() [ "ptrauth"(i32 1, i64 [[V1]]) ]
; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = load ptr, ptr %pp, align 8
%v1 = ptrtoint ptr %pp to i64
%v2 = ptrtoint ptr %v0 to i64
%v3 = call i64 @llvm.ptrauth.blend(i64 %v1, i64 5678)
%v4 = call i64 @llvm.ptrauth.resign(i64 %v2, i32 1, i64 1234, i32 1, i64 %v3)
%v5 = inttoptr i64 %v4 to ptr
%v6 = call i32 %v5() [ "ptrauth"(i32 1, i64 %v1) ]
ret i32 %v6
}
define i32 @test_ptrauth_call_resign_changing_call_key(ptr %p) {
; CHECK-LABEL: @test_ptrauth_call_resign_changing_call_key(
; CHECK-NEXT: [[V0:%.*]] = ptrtoint ptr [[P:%.*]] to i64
; CHECK-NEXT: [[V1:%.*]] = call i64 @llvm.ptrauth.resign(i64 [[V0]], i32 2, i64 1234, i32 1, i64 5678)
; CHECK-NEXT: [[V2:%.*]] = inttoptr i64 [[V1]] to ptr
; CHECK-NEXT: [[V3:%.*]] = call i32 [[V2]]() [ "ptrauth"(i32 1, i64 5678) ]
; CHECK-NEXT: ret i32 [[V3]]
;
%v0 = ptrtoint ptr %p to i64
%v1 = call i64 @llvm.ptrauth.resign(i64 %v0, i32 2, i64 1234, i32 1, i64 5678)
%v2 = inttoptr i64 %v1 to ptr
%v3 = call i32 %v2() [ "ptrauth"(i32 1, i64 5678) ]
ret i32 %v3
}
declare i64 @llvm.ptrauth.sign(i64, i32, i64)
declare i64 @llvm.ptrauth.resign(i64, i32, i64, i32, i64)
declare i64 @llvm.ptrauth.blend(i64, i64)