llvm_vararg_ty],
[Throws]>;
+
+//===------------------------ Garbage Collection Intrinsics ---------------===//
+// These are documented in docs/Statepoint.rst
+
+def int_experimental_gc_statepoint : Intrinsic<[llvm_i32_ty],
+ [llvm_anyptr_ty, llvm_i32_ty,
+ llvm_i32_ty, llvm_vararg_ty]>;
+
+def int_experimental_gc_result_int : Intrinsic<[llvm_anyint_ty], [llvm_i32_ty]>;
+def int_experimental_gc_result_float : Intrinsic<[llvm_anyfloat_ty],
+ [llvm_i32_ty]>;
+def int_experimental_gc_result_ptr : Intrinsic<[llvm_anyptr_ty], [llvm_i32_ty]>;
+
+def int_experimental_gc_relocate : Intrinsic<[llvm_anyptr_ty],
+ [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty]>;
+
//===-------------------------- Other Intrinsics --------------------------===//
//
def int_flt_rounds : Intrinsic<[llvm_i32_ty]>,
Assert1(isa<ConstantInt>(CI.getArgOperand(1)),
"llvm.invariant.end parameter #2 must be a constant integer", &CI);
break;
+
+ case Intrinsic::experimental_gc_statepoint: {
+ // target, # call args = 0, # deopt args = 0, #gc args = 0 -> 4 args
+ assert(CI.getNumArgOperands() >= 4 &&
+ "not enough arguments to statepoint");
+ for (User* U : CI.users()) {
+ const CallInst* GCRelocCall = cast<const CallInst>(U);
+ const Function *GCRelocFn = GCRelocCall->getCalledFunction();
+ Assert1(GCRelocFn && GCRelocFn->isDeclaration() &&
+ (GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_int ||
+ GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_float ||
+ GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_ptr ||
+ GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_relocate),
+ "gc.result or gc.relocate are the only value uses of statepoint", &CI);
+ if (GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_int ||
+ GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_float ||
+ GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_result_ptr ) {
+ Assert1(GCRelocCall->getNumArgOperands() == 1, "wrong number of arguments", &CI);
+ Assert2(GCRelocCall->getArgOperand(0) == &CI, "connected to wrong statepoint", &CI, GCRelocCall);
+ } else if (GCRelocFn->getIntrinsicID() == Intrinsic::experimental_gc_relocate) {
+ Assert1(GCRelocCall->getNumArgOperands() == 3, "wrong number of arguments", &CI);
+ Assert2(GCRelocCall->getArgOperand(0) == &CI, "connected to wrong statepoint", &CI, GCRelocCall);
+ } else {
+ llvm_unreachable("unsupported use type - how'd we get past the assert?");
+ }
+ }
+
+ // Note: It is legal for a single derived pointer to be listed multiple
+ // times. It's non-optimal, but it is legal. It can also happen after
+ // insertion if we strip a bitcast away.
+ // Note: It is really tempting to check that each base is relocated and
+ // that a derived pointer is never reused as a base pointer. This turns
+ // out to be problematic since optimizations run after safepoint insertion
+ // can recognize equality properties that the insertion logic doesn't know
+ // about. See example statepoint.ll in the verifier subdirectory
+ break;
+ }
+ case Intrinsic::experimental_gc_result_int:
+ case Intrinsic::experimental_gc_result_float:
+ case Intrinsic::experimental_gc_result_ptr: {
+ Assert1(CI.getNumArgOperands() == 1, "wrong number of arguments", &CI);
+
+ // Are we tied to a statepoint properly?
+ CallSite StatepointCS(CI.getArgOperand(0));
+ const Function *StatepointFn = StatepointCS.getCalledFunction();
+ Assert2(StatepointFn && StatepointFn->isDeclaration() &&
+ StatepointFn->getIntrinsicID() == Intrinsic::experimental_gc_statepoint,
+ "token must be from a statepoint", &CI, CI.getArgOperand(0));
+ break;
}
+ case Intrinsic::experimental_gc_relocate: {
+ // Some checks to ensure gc.relocate has the correct set of
+ // parameters. TODO: we can make these tests much stricter.
+ Assert1(CI.getNumArgOperands() == 3, "wrong number of arguments", &CI);
+
+ // Are we tied to a statepoint properly?
+ CallSite StatepointCS(CI.getArgOperand(0));
+ const Function *StatepointFn =
+ StatepointCS.getInstruction() ? StatepointCS.getCalledFunction() : NULL;
+ Assert2(StatepointFn && StatepointFn->isDeclaration() &&
+ StatepointFn->getIntrinsicID() == Intrinsic::experimental_gc_statepoint,
+ "token must be from a statepoint", &CI, CI.getArgOperand(0));
+
+ // Both the base and derived must be piped through the safepoint
+ Value* Base = CI.getArgOperand(1);
+ Assert1( isa<ConstantInt>(Base), "must be integer offset", &CI);
+
+ Value* Derived = CI.getArgOperand(2);
+ Assert1( isa<ConstantInt>(Derived), "must be integer offset", &CI);
+
+ const int BaseIndex = cast<ConstantInt>(Base)->getZExtValue();
+ const int DerivedIndex = cast<ConstantInt>(Derived)->getZExtValue();
+ // Check the bounds
+ Assert1(0 <= BaseIndex &&
+ BaseIndex < (int)StatepointCS.arg_size(),
+ "index out of bounds", &CI);
+ Assert1(0 <= DerivedIndex &&
+ DerivedIndex < (int)StatepointCS.arg_size(),
+ "index out of bounds", &CI);
+ break;
+ }
+ };
}
void DebugInfoVerifier::verifyDebugInfo() {
--- /dev/null
+; RUN: opt -S %s -verify | FileCheck %s
+
+declare void @use(...)
+declare i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32, i32, i32)
+declare i32 @llvm.statepoint.p0f_isVoidf(void ()*, i32, i32, ...)
+
+;; Basic usage
+define i8 addrspace(1)* @test1(i8 addrspace(1)* %arg) {
+entry:
+ %cast = bitcast i8 addrspace(1)* %arg to i64 addrspace(1)*
+ %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)
+ %reloc = call i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32 %safepoint_token, i32 9, i32 10)
+ ;; It is perfectly legal to relocate the same value multiple times...
+ %reloc2 = call i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32 %safepoint_token, i32 9, i32 10)
+ %reloc3 = call i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32 %safepoint_token, i32 10, i32 9)
+ ret i8 addrspace(1)* %reloc
+; CHECK-LABEL: test1
+; CHECK: statepoint
+; CHECK: gc.relocate
+; CHECK: gc.relocate
+; CHECK: gc.relocate
+; CHECK: ret i8 addrspace(1)* %reloc
+}
+
+; This test catches two cases where the verifier was too strict:
+; 1) A base doesn't need to be relocated if it's never used again
+; 2) A value can be replaced by one which is known equal. This
+; means a potentially derived pointer can be known base and that
+; we can't check that derived pointer are never bases.
+define void @test2(i8 addrspace(1)* %arg, i64 addrspace(1)* %arg2) {
+entry:
+ %cast = bitcast i8 addrspace(1)* %arg to i64 addrspace(1)*
+ %c = icmp eq i64 addrspace(1)* %cast, %arg2
+ br i1 %c, label %equal, label %notequal
+
+notequal:
+ ret void
+
+equal:
+%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)
+ %reloc = call i8 addrspace(1)* @llvm.gc.relocate.p1i8(i32 %safepoint_token, i32 9, i32 10)
+ call void undef(i8 addrspace(1)* %reloc)
+ ret void
+; CHECK-LABEL: test2
+; CHECK-LABEL: equal
+; CHECK: statepoint
+; CHECK-NEXT: %reloc = call
+; CHECK-NEXT: call
+; CHECK-NEXT: ret voi
+}