Skip to content

Commit 6554412

Browse files
committed
[Coroutines] Support ptrauth.
1 parent 6981eb5 commit 6554412

File tree

5 files changed

+216
-6
lines changed

5 files changed

+216
-6
lines changed

llvm/lib/Transforms/Coroutines/CoroInstr.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#ifndef LLVM_LIB_TRANSFORMS_COROUTINES_COROINSTR_H
2626
#define LLVM_LIB_TRANSFORMS_COROUTINES_COROINSTR_H
2727

28+
#include "llvm/IR/GlobalPtrAuthInfo.h"
2829
#include "llvm/IR/GlobalVariable.h"
2930
#include "llvm/IR/IntrinsicInst.h"
3031
#include "llvm/Support/raw_ostream.h"
@@ -223,7 +224,16 @@ class LLVM_LIBRARY_VISIBILITY AnyCoroIdRetconInst : public AnyCoroIdInst {
223224
/// attributes, and calling convention of the continuation function(s)
224225
/// are taken from this declaration.
225226
Function *getPrototype() const {
226-
return cast<Function>(getArgOperand(PrototypeArg)->stripPointerCasts());
227+
Value *rawPrototype = getArgOperand(PrototypeArg)->stripPointerCasts();
228+
if (auto global = GlobalPtrAuthInfo::analyze(rawPrototype)) {
229+
rawPrototype =
230+
const_cast<Constant*>(global->getPointer()->stripPointerCasts());
231+
}
232+
return cast<Function>(rawPrototype);
233+
}
234+
235+
std::optional<GlobalPtrAuthInfo> getPtrAuthInfo() const {
236+
return GlobalPtrAuthInfo::analyze(getArgOperand(PrototypeArg));
227237
}
228238

229239
/// Return the function to use for allocating memory.

llvm/lib/Transforms/Coroutines/CoroInternal.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
119119
};
120120

121121
struct RetconLoweringStorage {
122+
const Constant *ResumePtrAuthInfo;
122123
Function *ResumePrototype;
123124
Function *Alloc;
124125
Function *Dealloc;
@@ -175,10 +176,22 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
175176
return ConstantInt::get(getIndexType(), Value);
176177
}
177178

179+
std::optional<GlobalPtrAuthInfo> getResumePtrAuthInfo() const {
180+
switch (ABI) {
181+
case coro::ABI::Switch:
182+
case coro::ABI::Async:
183+
return std::nullopt;
184+
case coro::ABI::Retcon:
185+
case coro::ABI::RetconOnce:
186+
if (!RetconLowering.ResumePtrAuthInfo) return std::nullopt;
187+
return GlobalPtrAuthInfo::analyze(RetconLowering.ResumePtrAuthInfo);
188+
}
189+
}
190+
178191
PointerType *getSwitchResumePointerType() const {
179192
assert(ABI == coro::ABI::Switch);
180-
assert(FrameTy && "frame type not assigned");
181-
return cast<PointerType>(FrameTy->getElementType(SwitchFieldIndex::Resume));
193+
assert(FrameTy && "frame type not assigned");
194+
return cast<PointerType>(FrameTy->getElementType(SwitchFieldIndex::Resume));
182195
}
183196

184197
FunctionType *getResumeFunctionType() const {

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,19 @@ void CoroCloner::create() {
937937
VMap[&A] = DummyArgs.back();
938938
}
939939

940+
switch (Shape.ABI) {
941+
case coro::ABI::Retcon:
942+
case coro::ABI::RetconOnce:
943+
// In retcon coroutines, replace any remaining uses of the original
944+
// storage value with the storage parameter.
945+
VMap[Shape.getRetconCoroId()->getStorage()] = &*NewF->arg_begin();
946+
break;
947+
948+
case coro::ABI::Switch:
949+
case coro::ABI::Async:
950+
break;
951+
}
952+
940953
SmallVector<ReturnInst *, 4> Returns;
941954

942955
// Ignore attempts to change certain attributes of the function.
@@ -1091,8 +1104,10 @@ void CoroCloner::create() {
10911104

10921105
// Remap frame pointer.
10931106
Value *OldFramePtr = VMap[Shape.FramePtr];
1094-
NewFramePtr->takeName(OldFramePtr);
1095-
OldFramePtr->replaceAllUsesWith(NewFramePtr);
1107+
if (OldFramePtr != NewFramePtr) {
1108+
NewFramePtr->takeName(OldFramePtr);
1109+
OldFramePtr->replaceAllUsesWith(NewFramePtr);
1110+
}
10961111

10971112
// Remap vFrame pointer.
10981113
auto *NewVFrame = Builder.CreateBitCast(
@@ -1846,6 +1861,46 @@ static void splitAsyncCoroutine(Function &F, coro::Shape &Shape,
18461861
}
18471862
}
18481863

1864+
/// Produced a signed pointer value with this same schema. If the address
1865+
/// is provided, and this schema uses address discrimination, use that
1866+
/// address instead of the stored address discriminator.
1867+
static Value *emitSignedPointer(IRBuilder<> &Builder,
1868+
const GlobalPtrAuthInfo &Schema,
1869+
Constant *ConstPointer,
1870+
Value *Address) {
1871+
auto Module = Builder.GetInsertBlock()->getModule();
1872+
if (!Schema.hasAddressDiversity()) {
1873+
return Schema.createWithSameSchema(*Module, ConstPointer);
1874+
}
1875+
1876+
auto IntPtrTy = Schema.getAddrDiscriminator()->getType();
1877+
1878+
// Blend the provided address with the extra discriminator if the extra
1879+
// discriminator is non-zero.
1880+
Value *Discriminator = Builder.CreatePtrToInt(Address, IntPtrTy);
1881+
auto ExtraDiscriminator = const_cast<ConstantInt*>(Schema.getDiscriminator());
1882+
if (!ExtraDiscriminator->isNullValue()) {
1883+
auto Blend = Intrinsic::getDeclaration(Module, Intrinsic::ptrauth_blend);
1884+
Discriminator =
1885+
Builder.CreateCall(Blend, {Discriminator, ExtraDiscriminator});
1886+
}
1887+
1888+
// Sign the pointer with the discriminator.
1889+
1890+
// The intrinsic wants an integer.
1891+
Value *Pointer = Builder.CreatePtrToInt(ConstPointer, IntPtrTy);
1892+
1893+
// call i64 @llvm.ptrauth.sign.i64(i64 %pointer, i32 %key, i64 %discriminator)
1894+
auto Sign = Intrinsic::getDeclaration(Module, Intrinsic::ptrauth_sign);
1895+
auto Key = const_cast<ConstantInt*>(Schema.getKey());
1896+
Pointer = Builder.CreateCall(Sign, { Pointer, Key, Discriminator });
1897+
1898+
// Convert back to the original pointer type.
1899+
Pointer = Builder.CreateIntToPtr(Pointer, ConstPointer->getType());
1900+
1901+
return Pointer;
1902+
}
1903+
18491904
static void splitRetconCoroutine(Function &F, coro::Shape &Shape,
18501905
SmallVectorImpl<Function *> &Clones) {
18511906
assert(Shape.ABI == coro::ABI::Retcon ||
@@ -1957,9 +2012,21 @@ static void splitRetconCoroutine(Function &F, coro::Shape &Shape,
19572012
Builder.CreateRet(RetV);
19582013
}
19592014

2015+
// Sign the continuation value if requested.
2016+
Value *ContinuationValue = Continuation;
2017+
if (auto PtrAuthInfo = Shape.getResumePtrAuthInfo()) {
2018+
assert(!PtrAuthInfo->hasAddressDiversity() ||
2019+
PtrAuthInfo->hasSpecialAddressDiscriminator(
2020+
GlobalPtrAuthInfo::AddrDiscriminator_UseCoroStorage));
2021+
IRBuilder<> Builder(Branch);
2022+
ContinuationValue =
2023+
emitSignedPointer(Builder, *PtrAuthInfo, Continuation,
2024+
Id->getStorage());
2025+
}
2026+
19602027
// Branch to the return block.
19612028
Branch->setSuccessor(0, ReturnBB);
1962-
ReturnPHIs[0]->addIncoming(Continuation, SuspendBB);
2029+
ReturnPHIs[0]->addIncoming(ContinuationValue, SuspendBB);
19632030
size_t NextPHIIndex = 1;
19642031
for (auto &VUse : Suspend->value_operands())
19652032
ReturnPHIs[NextPHIIndex++]->addIncoming(&*VUse, SuspendBB);

llvm/lib/Transforms/Coroutines/Coroutines.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,11 +337,23 @@ void coro::Shape::buildFrom(Function &F) {
337337
: coro::ABI::RetconOnce);
338338
auto Prototype = ContinuationId->getPrototype();
339339
this->RetconLowering.ResumePrototype = Prototype;
340+
auto AuthInfo = ContinuationId->getPtrAuthInfo();
341+
this->RetconLowering.ResumePtrAuthInfo = (AuthInfo ? AuthInfo->getGV() : nullptr);
340342
this->RetconLowering.Alloc = ContinuationId->getAllocFunction();
341343
this->RetconLowering.Dealloc = ContinuationId->getDeallocFunction();
342344
this->RetconLowering.ReturnBlock = nullptr;
343345
this->RetconLowering.IsFrameInlineInStorage = false;
344346

347+
if (AuthInfo && AuthInfo->hasAddressDiversity() &&
348+
!AuthInfo->hasSpecialAddressDiscriminator(
349+
GlobalPtrAuthInfo::AddrDiscriminator_UseCoroStorage)) {
350+
#ifndef NDEBUG
351+
AuthInfo->getGV()->dump();
352+
#endif
353+
report_fatal_error("ptrauth-signed prototype must not have address "
354+
"diversity");
355+
}
356+
345357
// Determine the result value types, and make sure they match up with
346358
// the values passed to the suspends.
347359
auto ResultTys = getRetconResultTypes();
@@ -512,6 +524,9 @@ void coro::Shape::emitDealloc(IRBuilder<> &Builder, Value *Ptr,
512524
/// Check that the given value is a well-formed prototype for the
513525
/// llvm.coro.id.retcon.* intrinsics.
514526
static void checkWFRetconPrototype(const AnyCoroIdRetconInst *I, Value *V) {
527+
if (auto PtrAuth = GlobalPtrAuthInfo::analyze(V)) {
528+
V = const_cast<Constant*>(PtrAuth->getPointer());
529+
}
515530
auto F = dyn_cast<Function>(V->stripPointerCasts());
516531
if (!F)
517532
fail(I, "llvm.coro.id.retcon.* prototype not a Function", V);
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
; RUN: opt -passes='module(coro-early),cgscc(coro-split),coro-cleanup' -S %s | FileCheck %s
2+
target datalayout = "E-p:64:64"
3+
4+
%swift.type = type { i64 }
5+
%swift.opaque = type opaque
6+
%T4red215EmptyCollectionV = type opaque
7+
%TSi = type <{ i64 }>
8+
9+
@prototype.signed = private constant { ptr, i32, i64, i64 } { ptr @prototype, i32 2, i64 0, i64 12867 }, section "llvm.ptrauth"
10+
11+
define ptr @f(ptr %buffer, i32 %n) {
12+
entry:
13+
%id = call token @llvm.coro.id.retcon(i32 8, i32 4, ptr %buffer, ptr @prototype.signed, ptr @allocate, ptr @deallocate)
14+
%hdl = call ptr @llvm.coro.begin(token %id, ptr null)
15+
br label %loop
16+
17+
loop:
18+
%n.val = phi i32 [ %n, %entry ], [ %inc, %resume ]
19+
call void @print(i32 %n.val)
20+
%unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1()
21+
br i1 %unwind0, label %cleanup, label %resume
22+
23+
resume:
24+
%inc = add i32 %n.val, 1
25+
br label %loop
26+
27+
cleanup:
28+
call i1 @llvm.coro.end(ptr %hdl, i1 0)
29+
unreachable
30+
}
31+
32+
; CHECK: @prototype.signed = private constant
33+
; CHECK: [[GLOBAL:@.*]] = private constant { ptr, i32, i64, i64 } { ptr [[RESUME:@.*]], i32 2, i64 0, i64 12867 }, section "llvm.ptrauth"
34+
35+
; CHECK-LABEL: define ptr @f(ptr %buffer, i32 %n)
36+
; CHECK-NEXT: entry:
37+
; CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[FRAME_T:%.*]], ptr %buffer, i32 0, i32 0
38+
; CHECK-NEXT: store i32 %n, ptr [[T1]]
39+
; CHECK-NEXT: call void @print(i32 %n)
40+
; CHECK-NEXT: ret ptr [[GLOBAL]]
41+
42+
; CHECK: define internal ptr [[RESUME]](ptr noalias noundef nonnull align 4 dereferenceable(8) %0, i1 zeroext %1) {
43+
; CHECK: [[GLOBAL]]
44+
45+
@g.prototype.signed = private constant { ptr, i32, i64, i64 } { ptr @prototype, i32 2, i64 1, i64 8723 }, section "llvm.ptrauth"
46+
47+
define ptr @g(ptr %buffer, i32 %n) {
48+
entry:
49+
%id = call token @llvm.coro.id.retcon(i32 8, i32 4, ptr %buffer, ptr @g.prototype.signed, ptr @allocate, ptr @deallocate)
50+
%hdl = call ptr @llvm.coro.begin(token %id, ptr null)
51+
br label %loop
52+
53+
loop:
54+
%n.val = phi i32 [ %n, %entry ], [ %inc, %resume ]
55+
call void @print(i32 %n.val)
56+
%unwind0 = call i1 (...) @llvm.coro.suspend.retcon.i1()
57+
br i1 %unwind0, label %cleanup, label %resume
58+
59+
resume:
60+
%inc = add i32 %n.val, 1
61+
br label %loop
62+
63+
cleanup:
64+
call i1 @llvm.coro.end(ptr %hdl, i1 0)
65+
unreachable
66+
}
67+
68+
; CHECK-LABEL: define ptr @g(ptr %buffer, i32 %n)
69+
; CHECK-NEXT: entry:
70+
; CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[FRAME_T:%.*]], ptr %buffer, i32 0, i32 0
71+
; CHECK-NEXT: store i32 %n, ptr [[T1]]
72+
; CHECK-NEXT: call void @print(i32 %n)
73+
; CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr %buffer to i64
74+
; CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 8723)
75+
; CHECK-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr [[RESUME:@.*]] to i64), i32 2, i64 [[T1]])
76+
; CHECK-NEXT: [[T3:%.*]] = inttoptr i64 [[T2]] to ptr
77+
; CHECK-NEXT: ret ptr [[T3]]
78+
79+
; CHECK: define internal ptr [[RESUME]](ptr noalias noundef nonnull align 4 dereferenceable(8) %0, i1 zeroext %1) {
80+
81+
; CHECK: common.ret:
82+
; CHECK: [[RETVAL:%.*]] = phi ptr [ [[T4:%.*]], %resume ], [ null,
83+
; CHECK: ret ptr [[RETVAL]]
84+
85+
; CHECK: call void @print(i32 %inc)
86+
; CHECK-NEXT: [[T0:%.*]] = ptrtoint ptr %0 to i64
87+
; CHECK-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[T0]], i64 8723)
88+
; CHECK-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 ptrtoint (ptr [[RESUME]] to i64), i32 2, i64 [[T1]])
89+
; CHECK-NEXT: [[T3:%.*]] = inttoptr i64 [[T2]] to ptr
90+
91+
declare noalias ptr @malloc(i64) #5
92+
declare void @free(ptr nocapture) #5
93+
94+
declare token @llvm.coro.id.retcon(i32, i32, ptr, ptr, ptr, ptr)
95+
declare ptr @llvm.coro.begin(token, ptr)
96+
declare i1 @llvm.coro.suspend.retcon.i1(...)
97+
declare i1 @llvm.coro.end(ptr, i1)
98+
declare ptr @llvm.coro.prepare.retcon(ptr)
99+
100+
declare ptr @prototype(ptr, i1 zeroext)
101+
102+
declare noalias ptr @allocate(i32 %size)
103+
declare void @deallocate(ptr %ptr)
104+
105+
declare void @print(i32)

0 commit comments

Comments
 (0)