Remove deopt and gc transition arguments from gc.statepoint intrinsic

(Forgot to land this a couple of weeks back.)

In a recent series of changes, I've introduced support for using the respective operand bundle kinds on the statepoint. At the moment, code supports either/or, but there's no need to keep the old support around. For the moment, I am simply changing the specification and verifier to require zero length argument sets in the intrinsic.

The intrinsic itself is experimental. Given that, there's no forward serialization needed. The in tree uses and generation have already been updated to use the new operand bundle based forms, the only folks broken by the change will be those with frontends generating statepoints directly and the updates should be easy.

Why not go ahead and just remove the arguments entirely? Well, I plan to. But while working on this I've found that almost all of the arguments to the statepoint can be expressed via operand bundles or attributes. Given that, I'm planning a radical simplification of the arguments and figured I'd do one update not several small ones.

Differential Revision: https://reviews.llvm.org/D80892
This commit is contained in:
Philip Reames
2020-08-14 16:06:19 -07:00
parent 72effd8d5b
commit a96fc4638b
5 changed files with 35 additions and 58 deletions

View File

@@ -2182,6 +2182,12 @@ site, these bundles may contain any values that are needed by the
generated code. For more details, see :ref:`GC Transitions
<gc_transition_args>`.
The bundle contain an arbitrary list of Values which need to be passed
to GC transition code. They will be lowered and passed as operands to
the appropriate GC_TRANSITION nodes in the selection DAG. It is assumed
that these arguments must be available before and after (but not
necessarily during) the execution of the callee.
.. _assume_opbundles:
Assume Operand Bundles

View File

@@ -449,8 +449,7 @@ Syntax:
func_type <target>,
i64 <#call args>, i64 <flags>,
... (call parameters),
i64 <# transition args>, ... (transition parameters),
i64 <# deopt args>, ... (deopt parameters),
i64 0, i64 0,
... (gc parameters))
Overview:
@@ -515,17 +514,11 @@ instruction. The number of arguments must exactly match what is
specified in '# call args'. The types must match the signature of
'target'.
The 'transition parameters' arguments contain an arbitrary list of
Values which need to be passed to GC transition code. They will be
lowered and passed as operands to the appropriate GC_TRANSITION nodes
in the selection DAG. It is assumed that these arguments must be
available before and after (but not necessarily during) the execution
of the callee. The '# transition args' field indicates how many operands
are to be interpreted as 'transition parameters'.
The 'deopt parameters' arguments contain an arbitrary list of Values
which is meaningful to the runtime. The '# deopt args' field
indicates how many operands are to be interpreted as 'deopt parameters'.
The 'call parameter' attributes must be followed by two 'i64 0' constants.
These were originally the length prefixes for 'gc transition parameter' and
'deopt parameter' arguments, but the role of these parameter sets have been
entirely replaced with the corresponding operand bundles. In a future
revision, these now redundant arguments will be removed.
The 'gc parameters' arguments contain every pointer to a garbage
collector object which potentially needs to be updated by the garbage
@@ -676,13 +669,12 @@ Each statepoint generates the following Locations:
these identifiers.
* Constant which describes the flags passed to the statepoint intrinsic
* Constant which describes number of following deopt *Locations* (not
operands)
* Variable number of Locations, one for each deopt parameter listed in
the IR statepoint (same number as described by previous Constant). At
the moment, only deopt parameters with a bitwidth of 64 bits or less
are supported. Values of a type larger than 64 bits can be specified
and reported only if a) the value is constant at the call site, and b)
the constant can be represented with less than 64 bits (assuming zero
operands). Will be 0 if no "deopt" bundle is provided.
* Variable number of Locations, one for each deopt parameter listed in the
"deopt" operand bundle. At the moment, only deopt parameters with a bitwidth
of 64 bits or less are supported. Values of a type larger than 64 bits can be
specified and reported only if a) the value is constant at the call site, and
b) the constant can be represented with less than 64 bits (assuming zero
extension to the original bitwidth).
* Variable number of relocation records, each of which consists of
exactly two Locations. Relocation records are described in detail

View File

@@ -499,19 +499,14 @@ static MachineInstr *foldPatchpoint(MachineFunction &MF, MachineInstr &MI,
// Return false if any operands requested for folding are not foldable (not
// part of the stackmap's live values).
for (unsigned Op : Ops) {
// Caller is expected to avoid passing in tied operands
assert(!MI.getOperand(Op).isTied());
if (Op < NumDefs) {
assert(DefToFoldIdx == MI.getNumOperands() && "Folding multiple defs");
DefToFoldIdx = Op;
} else if (Op < StartIdx) {
return nullptr;
}
// When called from regalloc (InlineSpiller), operands must be untied,
// and regalloc will take care of (re)loading operand from memory.
// But when called from other places (e.g. peephole pass),
// we cannot fold operand which are tied - callers are unaware they
// need to reload destination register.
if (MI.getOperand(Op).isTied())
return nullptr;
}
MachineInstr *NewMI =

View File

@@ -2123,34 +2123,18 @@ void Verifier::verifyStatepoint(const CallBase &Call) {
Call);
const int NumTransitionArgs =
cast<ConstantInt>(NumTransitionArgsV)->getZExtValue();
Assert(NumTransitionArgs >= 0,
"gc.statepoint number of transition arguments must be positive", Call);
Assert(NumTransitionArgs == 0,
"gc.statepoint w/inline transition bundle is deprecated", Call);
const int EndTransitionArgsInx = EndCallArgsInx + 1 + NumTransitionArgs;
// We're migrating away from inline operands to operand bundles, enforce
// the either/or property during transition.
if (Call.getOperandBundle(LLVMContext::OB_gc_transition)) {
Assert(NumTransitionArgs == 0,
"can't use both deopt operands and deopt bundle on a statepoint");
}
const Value *NumDeoptArgsV = Call.getArgOperand(EndTransitionArgsInx + 1);
Assert(isa<ConstantInt>(NumDeoptArgsV),
"gc.statepoint number of deoptimization arguments "
"must be constant integer",
Call);
const int NumDeoptArgs = cast<ConstantInt>(NumDeoptArgsV)->getZExtValue();
Assert(NumDeoptArgs >= 0,
"gc.statepoint number of deoptimization arguments "
"must be positive",
Call);
// We're migrating away from inline operands to operand bundles, enforce
// the either/or property during transition.
if (Call.getOperandBundle(LLVMContext::OB_deopt)) {
Assert(NumDeoptArgs == 0,
"can't use both deopt operands and deopt bundle on a statepoint");
}
Assert(NumDeoptArgs == 0,
"gc.statepoint w/inline deopt operands is deprecated", Call);
const int ExpectedNumArgs =
7 + NumCallArgs + NumTransitionArgs + NumDeoptArgs;

View File

@@ -11,11 +11,11 @@ declare i32 @"personality_function"()
define i64 addrspace(1)* @test1(i8 addrspace(1)* %arg) gc "statepoint-example" {
entry:
%cast = bitcast i8 addrspace(1)* %arg to i64 addrspace(1)*
%safepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* undef, i32 0, i32 0, i32 0, i32 5, i32 0, i32 0, i32 0, i32 10, i32 0, i8 addrspace(1)* %arg, i64 addrspace(1)* %cast, i8 addrspace(1)* %arg, i8 addrspace(1)* %arg)
%reloc = call i64 addrspace(1)* @llvm.experimental.gc.relocate.p1i64(token %safepoint_token, i32 12, i32 13)
%safepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* undef, i32 0, i32 0, i32 0, i32 0, i8 addrspace(1)* %arg, i64 addrspace(1)* %cast, i8 addrspace(1)* %arg, i8 addrspace(1)* %arg) ["deopt" (i32 0, i32 0, i32 0, i32 10, i32 0)]
%reloc = call i64 addrspace(1)* @llvm.experimental.gc.relocate.p1i64(token %safepoint_token, i32 7, i32 8)
;; It is perfectly legal to relocate the same value multiple times...
%reloc2 = call i64 addrspace(1)* @llvm.experimental.gc.relocate.p1i64(token %safepoint_token, i32 12, i32 13)
%reloc3 = call i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %safepoint_token, i32 13, i32 12)
%reloc2 = call i64 addrspace(1)* @llvm.experimental.gc.relocate.p1i64(token %safepoint_token, i32 7, i32 8)
%reloc3 = call i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %safepoint_token, i32 8, i32 7)
ret i64 addrspace(1)* %reloc
; CHECK-LABEL: test1
; CHECK: statepoint
@@ -40,8 +40,8 @@ notequal:
ret void
equal:
%safepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* undef, i32 0, i32 0, i32 0, i32 5, i32 0, i32 0, i32 0, i32 10, i32 0, i8 addrspace(1)* %arg, i64 addrspace(1)* %cast, i8 addrspace(1)* %arg, i8 addrspace(1)* %arg)
%reloc = call i64 addrspace(1)* @llvm.experimental.gc.relocate.p1i64(token %safepoint_token, i32 12, i32 13)
%safepoint_token = call token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* undef, i32 0, i32 0, i32 0, i32 0, i8 addrspace(1)* %arg, i64 addrspace(1)* %cast, i8 addrspace(1)* %arg, i8 addrspace(1)* %arg) ["deopt" (i32 0, i32 0, i32 0, i32 10, i32 0)]
%reloc = call i64 addrspace(1)* @llvm.experimental.gc.relocate.p1i64(token %safepoint_token, i32 7, i32 7)
call void undef(i64 addrspace(1)* %reloc)
ret void
; CHECK-LABEL: test2
@@ -58,7 +58,7 @@ define i8 addrspace(1)* @test3(i8 addrspace(1)* %obj, i8 addrspace(1)* %obj1) gc
entry:
; CHECK-LABEL: entry
; CHECK: statepoint
%0 = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* undef, i32 0, i32 0, i32 0, i32 5, i32 0, i32 -1, i32 0, i32 0, i32 0, i8 addrspace(1)* %obj, i8 addrspace(1)* %obj1)
%0 = invoke token (i64, i32, void ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidf(i64 0, i32 0, void ()* undef, i32 0, i32 0, i32 0, i32 0, i8 addrspace(1)* %obj, i8 addrspace(1)* %obj1) ["deopt" (i32 0, i32 -1, i32 0, i32 0, i32 0)]
to label %normal_dest unwind label %exceptional_return
normal_dest:
@@ -66,8 +66,8 @@ normal_dest:
; CHECK: gc.relocate
; CHECK: gc.relocate
; CHECK: ret
%obj.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %0, i32 12, i32 12)
%obj1.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %0, i32 12, i32 12)
%obj.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %0, i32 7, i32 7)
%obj1.relocated = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %0, i32 8, i32 8)
ret i8 addrspace(1)* %obj.relocated
exceptional_return:
@@ -76,8 +76,8 @@ exceptional_return:
; CHECK: gc.relocate
%landing_pad = landingpad token
cleanup
%obj.relocated1 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %landing_pad, i32 12, i32 12)
%obj1.relocated1 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %landing_pad, i32 12, i32 12)
%obj.relocated1 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %landing_pad, i32 7, i32 7)
%obj1.relocated1 = call coldcc i8 addrspace(1)* @llvm.experimental.gc.relocate.p1i8(token %landing_pad, i32 8, i32 8)
ret i8 addrspace(1)* %obj1.relocated1
}