Skip to content

Commit e6e62ef

Browse files
authored
[RS4GC] Copy argument attributes from call to statepoint (#68475)
The current implementation completely ignores argument attributes on calls, discarding them completely when creating a statepoint from a call instruction. This is problematic in some scenarios as the argument attributes affect the ABI of the call, leading to undefined behavior if called with the wrong ABI attributes. Note that this cannot be solved either by just having the function declaration annotated with the right parameter attributes as the call might be indirect, therefore requiring them to be present on the arguments. This PR simply copies all parameter attributes over from the original call to the created statepoint. Note that some argument attributes become invalid after the lowering as they imply memory effects that no longer hold with the statepoints. These do not need to be explicitly handled in this PR as they are removed by the `stripNonValidDataFromBody`.
1 parent e200b0e commit e6e62ef

File tree

2 files changed

+74
-15
lines changed

2 files changed

+74
-15
lines changed

llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "llvm/ADT/DenseSet.h"
1919
#include "llvm/ADT/MapVector.h"
2020
#include "llvm/ADT/STLExtras.h"
21+
#include "llvm/ADT/Sequence.h"
2122
#include "llvm/ADT/SetVector.h"
2223
#include "llvm/ADT/SmallSet.h"
2324
#include "llvm/ADT/SmallVector.h"
@@ -1422,14 +1423,15 @@ static constexpr Attribute::AttrKind FnAttrsToStrip[] =
14221423
{Attribute::Memory, Attribute::NoSync, Attribute::NoFree};
14231424

14241425
// Create new attribute set containing only attributes which can be transferred
1425-
// from original call to the safepoint.
1426-
static AttributeList legalizeCallAttributes(LLVMContext &Ctx,
1427-
AttributeList OrigAL,
1426+
// from the original call to the safepoint.
1427+
static AttributeList legalizeCallAttributes(CallBase *Call, bool IsMemIntrinsic,
14281428
AttributeList StatepointAL) {
1429+
AttributeList OrigAL = Call->getAttributes();
14291430
if (OrigAL.isEmpty())
14301431
return StatepointAL;
14311432

14321433
// Remove the readonly, readnone, and statepoint function attributes.
1434+
LLVMContext &Ctx = Call->getContext();
14331435
AttrBuilder FnAttrs(Ctx, OrigAL.getFnAttrs());
14341436
for (auto Attr : FnAttrsToStrip)
14351437
FnAttrs.removeAttribute(Attr);
@@ -1439,8 +1441,24 @@ static AttributeList legalizeCallAttributes(LLVMContext &Ctx,
14391441
FnAttrs.removeAttribute(A);
14401442
}
14411443

1442-
// Just skip parameter and return attributes for now
1443-
return StatepointAL.addFnAttributes(Ctx, FnAttrs);
1444+
StatepointAL = StatepointAL.addFnAttributes(Ctx, FnAttrs);
1445+
1446+
// The memory intrinsics do not have a 1:1 correspondence of the original
1447+
// call arguments to the produced statepoint. Do not transfer the argument
1448+
// attributes to avoid putting them on incorrect arguments.
1449+
if (IsMemIntrinsic)
1450+
return StatepointAL;
1451+
1452+
// Attach the argument attributes from the original call at the corresponding
1453+
// arguments in the statepoint. Note that any argument attributes that are
1454+
// invalid after lowering are stripped in stripNonValidDataFromBody.
1455+
for (unsigned I : llvm::seq(Call->arg_size()))
1456+
StatepointAL = StatepointAL.addParamAttributes(
1457+
Ctx, GCStatepointInst::CallArgsBeginPos + I,
1458+
AttrBuilder(Ctx, OrigAL.getParamAttrs(I)));
1459+
1460+
// Return attributes are later attached to the gc.result intrinsic.
1461+
return StatepointAL;
14441462
}
14451463

14461464
/// Helper function to place all gc relocates necessary for the given
@@ -1630,6 +1648,7 @@ makeStatepointExplicitImpl(CallBase *Call, /* to replace */
16301648
// with a return value, we lower then as never returning calls to
16311649
// __llvm_deoptimize that are followed by unreachable to get better codegen.
16321650
bool IsDeoptimize = false;
1651+
bool IsMemIntrinsic = false;
16331652

16341653
StatepointDirectives SD =
16351654
parseStatepointDirectivesFromAttrs(Call->getAttributes());
@@ -1670,6 +1689,8 @@ makeStatepointExplicitImpl(CallBase *Call, /* to replace */
16701689
IsDeoptimize = true;
16711690
} else if (IID == Intrinsic::memcpy_element_unordered_atomic ||
16721691
IID == Intrinsic::memmove_element_unordered_atomic) {
1692+
IsMemIntrinsic = true;
1693+
16731694
// Unordered atomic memcpy and memmove intrinsics which are not explicitly
16741695
// marked as "gc-leaf-function" should be lowered in a GC parseable way.
16751696
// Specifically, these calls should be lowered to the
@@ -1785,12 +1806,10 @@ makeStatepointExplicitImpl(CallBase *Call, /* to replace */
17851806
SPCall->setTailCallKind(CI->getTailCallKind());
17861807
SPCall->setCallingConv(CI->getCallingConv());
17871808

1788-
// Currently we will fail on parameter attributes and on certain
1789-
// function attributes. In case if we can handle this set of attributes -
1790-
// set up function attrs directly on statepoint and return attrs later for
1809+
// Set up function attrs directly on statepoint and return attrs later for
17911810
// gc_result intrinsic.
1792-
SPCall->setAttributes(legalizeCallAttributes(
1793-
CI->getContext(), CI->getAttributes(), SPCall->getAttributes()));
1811+
SPCall->setAttributes(
1812+
legalizeCallAttributes(CI, IsMemIntrinsic, SPCall->getAttributes()));
17941813

17951814
Token = cast<GCStatepointInst>(SPCall);
17961815

@@ -1812,12 +1831,10 @@ makeStatepointExplicitImpl(CallBase *Call, /* to replace */
18121831

18131832
SPInvoke->setCallingConv(II->getCallingConv());
18141833

1815-
// Currently we will fail on parameter attributes and on certain
1816-
// function attributes. In case if we can handle this set of attributes -
1817-
// set up function attrs directly on statepoint and return attrs later for
1834+
// Set up function attrs directly on statepoint and return attrs later for
18181835
// gc_result intrinsic.
1819-
SPInvoke->setAttributes(legalizeCallAttributes(
1820-
II->getContext(), II->getAttributes(), SPInvoke->getAttributes()));
1836+
SPInvoke->setAttributes(
1837+
legalizeCallAttributes(II, IsMemIntrinsic, SPInvoke->getAttributes()));
18211838

18221839
Token = cast<GCStatepointInst>(SPInvoke);
18231840

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
2+
; RUN: opt < %s -passes=rewrite-statepoints-for-gc -S | FileCheck %s
3+
4+
declare i8 @callee(ptr, i8, float, ptr)
5+
6+
define i8 @test(ptr %arg) gc "statepoint-example" {
7+
; CHECK-LABEL: define i8 @test(
8+
; CHECK-SAME: ptr [[ARG:%.*]]) gc "statepoint-example" {
9+
; CHECK-NEXT: [[STATEPOINT_TOKEN:%.*]] = call token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(i8 (ptr, i8, float, ptr)) @callee, i32 4, i32 0, ptr nocapture sret({ i64, i64 }) align 8 null, i8 signext 8, float inreg 1.000000e+00, ptr [[ARG]], i32 0, i32 0)
10+
; CHECK-NEXT: [[R1:%.*]] = call zeroext i8 @llvm.experimental.gc.result.i8(token [[STATEPOINT_TOKEN]])
11+
; CHECK-NEXT: ret i8 [[R1]]
12+
;
13+
%r = call zeroext i8 @callee(ptr sret({i64, i64}) noalias align 8 nocapture null, i8 signext 8, float inreg 1.0, ptr writeonly %arg)
14+
ret i8 %r
15+
}
16+
17+
declare i32 @personality_function()
18+
19+
define i8 @test_invoke(ptr %arg) gc "statepoint-example" personality ptr @personality_function {
20+
; CHECK-LABEL: define i8 @test_invoke(
21+
; CHECK-SAME: ptr [[ARG:%.*]]) gc "statepoint-example" personality ptr @personality_function {
22+
; CHECK-NEXT: [[STATEPOINT_TOKEN:%.*]] = invoke token (i64, i32, ptr, i32, i32, ...) @llvm.experimental.gc.statepoint.p0(i64 2882400000, i32 0, ptr elementtype(i8 (ptr, i8, float, ptr)) @callee, i32 4, i32 0, ptr nocapture sret({ i64, i64 }) align 8 null, i8 signext 8, float inreg 1.000000e+00, ptr [[ARG]], i32 0, i32 0)
23+
; CHECK-NEXT: to label [[NORMAL_RETURN:%.*]] unwind label [[EXCEPTIONAL_RETURN:%.*]]
24+
; CHECK: normal_return:
25+
; CHECK-NEXT: [[R1:%.*]] = call zeroext i8 @llvm.experimental.gc.result.i8(token [[STATEPOINT_TOKEN]])
26+
; CHECK-NEXT: ret i8 [[R1]]
27+
; CHECK: exceptional_return:
28+
; CHECK-NEXT: [[LANDING_PAD4:%.*]] = landingpad token
29+
; CHECK-NEXT: cleanup
30+
; CHECK-NEXT: ret i8 0
31+
;
32+
%r = invoke zeroext i8 @callee(ptr sret({i64, i64}) noalias align 8 nocapture null, i8 signext 8, float inreg 1.0, ptr writeonly %arg)
33+
to label %normal_return unwind label %exceptional_return
34+
35+
normal_return:
36+
ret i8 %r
37+
38+
exceptional_return:
39+
%landing_pad4 = landingpad token
40+
cleanup
41+
ret i8 0
42+
}

0 commit comments

Comments
 (0)