Skip to content

Commit 337c4bd

Browse files
committedDec 1, 2014
[Statepoints 1/4] Statepoint infrastructure for garbage collection: IR Intrinsics
The statepoint intrinsics are intended to enable precise root tracking through the compiler as to support garbage collectors of all types. The addition of the statepoint intrinsics to LLVM should have no impact on the compilation of any program which does not contain them. There are no side tables created, no extra metadata, and no inhibited optimizations. A statepoint works by transforming a call site (or safepoint poll site) into an explicit relocation operation. It is the frontend's responsibility (or eventually the safepoint insertion pass we've developed, but that's not part of this patch series) to ensure that any live pointer to a GC object is correctly added to the statepoint and explicitly relocated. The relocated value is just a normal SSA value (as seen by the optimizer), so merges of relocated and unrelocated values are just normal phis. The explicit relocation operation, the fact the statepoint is assumed to clobber all memory, and the optimizers standard semantics ensure that the relocations flow through IR optimizations correctly. This is the first patch in a small series. This patch contains only the IR parts; the documentation and backend support will be following separately. The entire series can be seen as one combined whole in http://reviews.llvm.org/D5683. Reviewed by: atrick, ributzka llvm-svn: 223078
1 parent 5b62eb9 commit 337c4bd

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed
 

‎llvm/include/llvm/IR/Intrinsics.td

+16
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,22 @@ def int_experimental_patchpoint_i64 : Intrinsic<[llvm_i64_ty],
493493
llvm_vararg_ty],
494494
[Throws]>;
495495

496+
497+
//===------------------------ Garbage Collection Intrinsics ---------------===//
498+
// These are documented in docs/Statepoint.rst
499+
500+
def int_experimental_gc_statepoint : Intrinsic<[llvm_i32_ty],
501+
[llvm_anyptr_ty, llvm_i32_ty,
502+
llvm_i32_ty, llvm_vararg_ty]>;
503+
504+
def int_experimental_gc_result_int : Intrinsic<[llvm_anyint_ty], [llvm_i32_ty]>;
505+
def int_experimental_gc_result_float : Intrinsic<[llvm_anyfloat_ty],
506+
[llvm_i32_ty]>;
507+
def int_experimental_gc_result_ptr : Intrinsic<[llvm_anyptr_ty], [llvm_i32_ty]>;
508+
509+
def int_experimental_gc_relocate : Intrinsic<[llvm_anyptr_ty],
510+
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty]>;
511+
496512
//===-------------------------- Other Intrinsics --------------------------===//
497513
//
498514
def int_flt_rounds : Intrinsic<[llvm_i32_ty]>,

‎llvm/lib/Analysis/TargetTransformInfo.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,10 @@ struct NoTTI final : ImmutablePass, TargetTransformInfo {
403403
case Intrinsic::objectsize:
404404
case Intrinsic::ptr_annotation:
405405
case Intrinsic::var_annotation:
406+
case Intrinsic::experimental_gc_result_int:
407+
case Intrinsic::experimental_gc_result_float:
408+
case Intrinsic::experimental_gc_result_ptr:
409+
case Intrinsic::experimental_gc_relocate:
406410
// These intrinsics don't actually represent code after lowering.
407411
return TCC_Free;
408412
}

‎llvm/lib/IR/Verifier.cpp

+81
Original file line numberDiff line numberDiff line change
@@ -2559,7 +2559,88 @@ void Verifier::visitIntrinsicFunctionCall(Intrinsic::ID ID, CallInst &CI) {
25592559
Assert1(isa<ConstantInt>(CI.getArgOperand(1)),
25602560
"llvm.invariant.end parameter #2 must be a constant integer", &CI);
25612561
break;
2562+
2563+
case Intrinsic::experimental_gc_statepoint: {
2564+
// target, # call args = 0, # deopt args = 0, #gc args = 0 -> 4 args
2565+
assert(CI.getNumArgOperands() >= 4 &&
2566+
"not enough arguments to statepoint");
2567+
for (User* U : CI.users()) {
2568+
const CallInst* GCRelocCall = cast<const CallInst>(U);
2569+
const Function *GCRelocFn = GCRelocCall->getCalledFunction();
2570+
Assert1(GCRelocFn && GCRelocFn->isDeclaration() &&
2571+
(GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_int ||
2572+
GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_float ||
2573+
GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_ptr ||
2574+
GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_relocate),
2575+
"gc.result or gc.relocate are the only value uses of statepoint", &CI);
2576+
if (GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_int ||
2577+
GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_float ||
2578+
GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_ptr ) {
2579+
Assert1(GCRelocCall->getNumArgOperands() == 1, "wrong number of arguments", &CI);
2580+
Assert2(GCRelocCall->getArgOperand(0) == &CI, "connected to wrong statepoint", &CI, GCRelocCall);
2581+
} else if (GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_relocate) {
2582+
Assert1(GCRelocCall->getNumArgOperands() == 3, "wrong number of arguments", &CI);
2583+
Assert2(GCRelocCall->getArgOperand(0) == &CI, "connected to wrong statepoint", &CI, GCRelocCall);
2584+
} else {
2585+
llvm_unreachable("unsupported use type - how'd we get past the assert?");
2586+
}
2587+
}
2588+
2589+
// Note: It is legal for a single derived pointer to be listed multiple
2590+
// times. It's non-optimal, but it is legal. It can also happen after
2591+
// insertion if we strip a bitcast away.
2592+
// Note: It is really tempting to check that each base is relocated and
2593+
// that a derived pointer is never reused as a base pointer. This turns
2594+
// out to be problematic since optimizations run after safepoint insertion
2595+
// can recognize equality properties that the insertion logic doesn't know
2596+
// about. See example statepoint.ll in the verifier subdirectory
2597+
break;
2598+
}
2599+
case Intrinsic::experimental_gc_result_int:
2600+
case Intrinsic::experimental_gc_result_float:
2601+
case Intrinsic::experimental_gc_result_ptr: {
2602+
Assert1(CI.getNumArgOperands() == 1, "wrong number of arguments", &CI);
2603+
2604+
// Are we tied to a statepoint properly?
2605+
CallSite StatepointCS(CI.getArgOperand(0));
2606+
const Function *StatepointFn = StatepointCS.getCalledFunction();
2607+
Assert2(StatepointFn && StatepointFn->isDeclaration() &&
2608+
StatepointFn->getIntrinsicID() == Intrinsic::experimental_gc_statepoint,
2609+
"token must be from a statepoint", &CI, CI.getArgOperand(0));
2610+
break;
25622611
}
2612+
case Intrinsic::experimental_gc_relocate: {
2613+
// Some checks to ensure gc.relocate has the correct set of
2614+
// parameters. TODO: we can make these tests much stricter.
2615+
Assert1(CI.getNumArgOperands() == 3, "wrong number of arguments", &CI);
2616+
2617+
// Are we tied to a statepoint properly?
2618+
CallSite StatepointCS(CI.getArgOperand(0));
2619+
const Function *StatepointFn =
2620+
StatepointCS.getInstruction() ? StatepointCS.getCalledFunction() : NULL;
2621+
Assert2(StatepointFn && StatepointFn->isDeclaration() &&
2622+
StatepointFn->getIntrinsicID() == Intrinsic::experimental_gc_statepoint,
2623+
"token must be from a statepoint", &CI, CI.getArgOperand(0));
2624+
2625+
// Both the base and derived must be piped through the safepoint
2626+
Value* Base = CI.getArgOperand(1);
2627+
Assert1( isa<ConstantInt>(Base), "must be integer offset", &CI);
2628+
2629+
Value* Derived = CI.getArgOperand(2);
2630+
Assert1( isa<ConstantInt>(Derived), "must be integer offset", &CI);
2631+
2632+
const int BaseIndex = cast<ConstantInt>(Base)->getZExtValue();
2633+
const int DerivedIndex = cast<ConstantInt>(Derived)->getZExtValue();
2634+
// Check the bounds
2635+
Assert1(0 <= BaseIndex &&
2636+
BaseIndex < (int)StatepointCS.arg_size(),
2637+
"index out of bounds", &CI);
2638+
Assert1(0 <= DerivedIndex &&
2639+
DerivedIndex < (int)StatepointCS.arg_size(),
2640+
"index out of bounds", &CI);
2641+
break;
2642+
}
2643+
};
25632644
}
25642645

25652646
void DebugInfoVerifier::verifyDebugInfo() {

‎llvm/test/Verifier/statepoint.ll

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
; RUN: opt -S %s -verify | FileCheck %s
2+
3+
declare void @use(...)
4+
declare i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32, i32, i32)
5+
declare i32 @llvm.statepoint.p0f_isVoidf(void ()*, i32, i32, ...)
6+
7+
;; Basic usage
8+
define i8 addrspace(1)* @test1(i8 addrspace(1)* %arg) {
9+
entry:
10+
%cast = bitcast i8 addrspace(1)* %arg to i64 addrspace(1)*
11+
%safepoint_token = call i32 (void ()*, i32, i32, ...)* @llvm.statepoint.p0f_isVoidf(void ()* undef, 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)
12+
%reloc = call i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32 %safepoint_token, i32 9, i32 10)
13+
;; It is perfectly legal to relocate the same value multiple times...
14+
%reloc2 = call i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32 %safepoint_token, i32 9, i32 10)
15+
%reloc3 = call i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32 %safepoint_token, i32 10, i32 9)
16+
ret i8 addrspace(1)* %reloc
17+
; CHECK-LABEL: test1
18+
; CHECK: statepoint
19+
; CHECK: gc.relocate
20+
; CHECK: gc.relocate
21+
; CHECK: gc.relocate
22+
; CHECK: ret i8 addrspace(1)* %reloc
23+
}
24+
25+
; This test catches two cases where the verifier was too strict:
26+
; 1) A base doesn't need to be relocated if it's never used again
27+
; 2) A value can be replaced by one which is known equal. This
28+
; means a potentially derived pointer can be known base and that
29+
; we can't check that derived pointer are never bases.
30+
define void @test2(i8 addrspace(1)* %arg, i64 addrspace(1)* %arg2) {
31+
entry:
32+
%cast = bitcast i8 addrspace(1)* %arg to i64 addrspace(1)*
33+
%c = icmp eq i64 addrspace(1)* %cast, %arg2
34+
br i1 %c, label %equal, label %notequal
35+
36+
notequal:
37+
ret void
38+
39+
equal:
40+
%safepoint_token = call i32 (void ()*, i32, i32, ...)* @llvm.statepoint.p0f_isVoidf(void ()* undef, 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)
41+
%reloc = call i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32 %safepoint_token, i32 9, i32 10)
42+
call void undef(i8 addrspace(1)* %reloc)
43+
ret void
44+
; CHECK-LABEL: test2
45+
; CHECK-LABEL: equal
46+
; CHECK: statepoint
47+
; CHECK-NEXT: %reloc = call
48+
; CHECK-NEXT: call
49+
; CHECK-NEXT: ret voi
50+
}

0 commit comments

Comments
 (0)
Please sign in to comment.