Add a "gc-transition" operand bundle
Summary:
This adds a new kind of operand bundle to LLVM denoted by the
`"gc-transition"` tag. Inputs to `"gc-transition"` operand bundle are
lowered into the "transition args" section of `gc.statepoint` by
`RewriteStatepointsForGC`.
This removes the last bit of functionality that was unsupported in the
deopt bundle based code path in `RewriteStatepointsForGC`.
Reviewers: pgavlin, JosephTremoulet, reames
Subscribers: sanjoy, mcrosier, llvm-commits
Differential Revision: http://reviews.llvm.org/D16342
llvm-svn: 258338
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 5f8a3a5..1932cb6 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1602,6 +1602,18 @@
Similarly, if no funclet EH pads have been entered-but-not-yet-exited,
executing a ``call`` or ``invoke`` with a ``"funclet"`` bundle is undefined behavior.
+GC Transition Operand Bundles
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+GC transition operand bundles are characterized by the
+``"gc-transition"`` operand bundle tag. These operand bundles mark a
+call as a transition between a function with one GC strategy to a
+function with a different GC strategy. If coordinating the transition
+between GC strategies requires additional code generation at the call
+site, these bundles may contain any values that are needed by the
+generated code. For more details, see :ref:`GC Transitions
+<gc_transition_args>`.
+
.. _moduleasm:
Module-Level Inline Assembly
diff --git a/llvm/docs/Statepoints.rst b/llvm/docs/Statepoints.rst
index 084aa62..b0e4f81 100644
--- a/llvm/docs/Statepoints.rst
+++ b/llvm/docs/Statepoints.rst
@@ -251,7 +251,9 @@
Note that in this example %p and %obj.relocate are the same address and we
could replace one with the other, potentially removing the derived pointer
-from the live set at the safepoint entirely.
+from the live set at the safepoint entirely.
+
+.. _gc_transition_args:
GC Transitions
^^^^^^^^^^^^^^^^^^
diff --git a/llvm/include/llvm/IR/LLVMContext.h b/llvm/include/llvm/IR/LLVMContext.h
index 56aa301..5b8512b 100644
--- a/llvm/include/llvm/IR/LLVMContext.h
+++ b/llvm/include/llvm/IR/LLVMContext.h
@@ -71,8 +71,9 @@
/// Additionally, this scheme allows LLVM to efficiently check for specific
/// operand bundle tags without comparing strings.
enum {
- OB_deopt = 0, // "deopt"
- OB_funclet = 1, // "funclet"
+ OB_deopt = 0, // "deopt"
+ OB_funclet = 1, // "funclet"
+ OB_gc_transition = 2, // "gc-transition"
};
/// getMDKindID - Return a unique non-zero ID for the specified metadata kind.
diff --git a/llvm/lib/IR/LLVMContext.cpp b/llvm/lib/IR/LLVMContext.cpp
index 48b53b0..5e6fe69 100644
--- a/llvm/lib/IR/LLVMContext.cpp
+++ b/llvm/lib/IR/LLVMContext.cpp
@@ -137,6 +137,11 @@
assert(FuncletEntry->second == LLVMContext::OB_funclet &&
"funclet operand bundle id drifted!");
(void)FuncletEntry;
+
+ auto *GCTransitionEntry = pImpl->getOrInsertBundleTag("gc-transition");
+ assert(GCTransitionEntry->second == LLVMContext::OB_gc_transition &&
+ "gc-transition operand bundle id drifted!");
+ (void)GCTransitionEntry;
}
LLVMContext::~LLVMContext() { delete pImpl; }
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 4018460..3dff4d1 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -2513,17 +2513,21 @@
if (Intrinsic::ID ID = (Intrinsic::ID)F->getIntrinsicID())
visitIntrinsicCallSite(ID, CS);
- // Verify that a callsite has at most one "deopt" and one "funclet" operand
- // bundle.
- bool FoundDeoptBundle = false, FoundFuncletBundle = false;
+ // Verify that a callsite has at most one "deopt", at most one "funclet" and
+ // at most one "gc-transition" operand bundle.
+ bool FoundDeoptBundle = false, FoundFuncletBundle = false,
+ FoundGCTransitionBundle = false;
for (unsigned i = 0, e = CS.getNumOperandBundles(); i < e; ++i) {
OperandBundleUse BU = CS.getOperandBundleAt(i);
uint32_t Tag = BU.getTagID();
if (Tag == LLVMContext::OB_deopt) {
Assert(!FoundDeoptBundle, "Multiple deopt operand bundles", I);
FoundDeoptBundle = true;
- }
- if (Tag == LLVMContext::OB_funclet) {
+ } else if (Tag == LLVMContext::OB_gc_transition) {
+ Assert(!FoundGCTransitionBundle, "Multiple gc-transition operand bundles",
+ I);
+ FoundGCTransitionBundle = true;
+ } else if (Tag == LLVMContext::OB_funclet) {
Assert(!FoundFuncletBundle, "Multiple funclet operand bundles", I);
FoundFuncletBundle = true;
Assert(BU.Inputs.size() == 1,
diff --git a/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp b/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
index e4e79c6..cefacbd 100644
--- a/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
+++ b/llvm/lib/Transforms/Scalar/RewriteStatepointsForGC.cpp
@@ -1404,8 +1404,11 @@
if (UseDeoptBundles) {
CallArgs = {CS.arg_begin(), CS.arg_end()};
DeoptArgs = GetDeoptBundleOperands(CS);
- // TODO: we don't fill in TransitionArgs or Flags in this branch, but we
- // could have an operand bundle for that too.
+ if (auto TransitionBundle =
+ CS.getOperandBundle(LLVMContext::OB_gc_transition)) {
+ Flags |= uint32_t(StatepointFlags::GCTransition);
+ TransitionArgs = TransitionBundle->Inputs;
+ }
AttributeSet OriginalAttrs = CS.getAttributes();
Attribute AttrID = OriginalAttrs.getAttribute(AttributeSet::FunctionIndex,
diff --git a/llvm/test/Transforms/RewriteStatepointsForGC/deopt-bundles/basic.ll b/llvm/test/Transforms/RewriteStatepointsForGC/deopt-bundles/basic.ll
index c0dc694..1881897d 100644
--- a/llvm/test/Transforms/RewriteStatepointsForGC/deopt-bundles/basic.ll
+++ b/llvm/test/Transforms/RewriteStatepointsForGC/deopt-bundles/basic.ll
@@ -63,3 +63,11 @@
%lpad = landingpad token cleanup
resume token undef
}
+
+define i32 addrspace(1)* @f4(i32 addrspace(1)* %arg) gc "statepoint-example" {
+; CHECK-LABEL: @f4(
+ entry:
+; CHECK: @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 2882400000, i32 0, void ()* @g, i32 0, i32 1, i32 2, i32 400, i8 90,
+ call void @g() [ "gc-transition"(i32 400, i8 90) ]
+ ret i32 addrspace(1)* %arg
+}
diff --git a/llvm/test/Verifier/operand-bundles.ll b/llvm/test/Verifier/operand-bundles.ll
index d822568..2598a78 100644
--- a/llvm/test/Verifier/operand-bundles.ll
+++ b/llvm/test/Verifier/operand-bundles.ll
@@ -47,3 +47,16 @@
%x = add i32 42, 1
ret void
}
+
+define void @f_gc_transition(i32* %ptr) {
+; CHECK: Multiple gc-transition operand bundles
+; CHECK-NEXT: call void @g() [ "gc-transition"(i32 42, i64 100, i32 %x), "gc-transition"(float 0.000000e+00, i64 100, i32 %l) ]
+; CHECK-NOT: call void @g() [ "gc-transition"(i32 42, i64 120, i32 %x) ]
+
+ entry:
+ %l = load i32, i32* %ptr
+ call void @g() [ "gc-transition"(i32 42, i64 100, i32 %x), "gc-transition"(float 0.0, i64 100, i32 %l) ]
+ call void @g() [ "gc-transition"(i32 42, i64 120) ] ;; The verifier should not complain about this one
+ %x = add i32 42, 1
+ ret void
+}