Index: include/llvm/IR/IRBuilder.h =================================================================== --- include/llvm/IR/IRBuilder.h +++ include/llvm/IR/IRBuilder.h @@ -441,10 +441,17 @@ /// \brief Create a call to the experimental.gc.statepoint intrinsic to /// start a new statepoint sequence. - CallInst *CreateGCStatepoint(Value *ActualCallee, - ArrayRef CallArgs, - ArrayRef DeoptArgs, - ArrayRef GCArgs, + CallInst *CreateGCStatepoint(Value *ActualCallee, ArrayRef CallArgs, + ArrayRef DeoptArgs, + ArrayRef GCArgs, + const Twine &Name = ""); + + // Conveninence function for the common case when CallArgs are filled in using + // makeArrayRef(CS.arg_begin(), .arg_end()); Use needs to be .get()'ed to get + // the Value *. + CallInst *CreateGCStatepoint(Value *ActualCallee, ArrayRef CallArgs, + ArrayRef DeoptArgs, + ArrayRef GCArgs, const Twine &Name = ""); /// \brief Create a call to the experimental.gc.result intrinsic to extract Index: lib/IR/IRBuilder.cpp =================================================================== --- lib/IR/IRBuilder.cpp +++ lib/IR/IRBuilder.cpp @@ -231,10 +231,10 @@ } CallInst *IRBuilderBase::CreateGCStatepoint(Value *ActualCallee, - ArrayRef CallArgs, - ArrayRef DeoptArgs, - ArrayRef GCArgs, - const Twine& Name) { + ArrayRef CallArgs, + ArrayRef DeoptArgs, + ArrayRef GCArgs, + const Twine &Name) { // Extract out the type of the callee. PointerType *FuncPtrType = cast(ActualCallee->getType()); assert(isa(FuncPtrType->getElementType()) && @@ -260,6 +260,17 @@ return createCallHelper(FnStatepoint, args, this, Name); } +CallInst *IRBuilderBase::CreateGCStatepoint(Value *ActualCallee, + ArrayRef CallArgs, + ArrayRef DeoptArgs, + ArrayRef GCArgs, + const Twine &Name) { + std::vector VCallArgs; + for (auto &U : CallArgs) + VCallArgs.push_back(U.get()); + return CreateGCStatepoint(ActualCallee, VCallArgs, DeoptArgs, GCArgs, Name); +} + CallInst *IRBuilderBase::CreateGCResult(Instruction *Statepoint, Type *ResultType, const Twine &Name) { Index: lib/Transforms/Scalar/PlaceSafepoints.cpp =================================================================== --- lib/Transforms/Scalar/PlaceSafepoints.cpp +++ lib/Transforms/Scalar/PlaceSafepoints.cpp @@ -738,7 +738,8 @@ // different type inserted previously Function *F = dyn_cast(M->getOrInsertFunction("gc.safepoint_poll", ftype)); - assert(F && !F->empty() && "definition must exist"); + assert(F && "void @gc.safepoint_poll() must be defined"); + assert(!F->empty() && "gc.safepoint_poll must be a non-empty function"); CallInst *poll = CallInst::Create(F, "", term); // Record some information about the call site we're replacing @@ -846,52 +847,33 @@ // this logic out to the initialization of the pass. Doesn't appear to // matter in practice. - // Fill in the one generic type'd argument (the function is also vararg) - std::vector argTypes; - argTypes.push_back(CS.getCalledValue()->getType()); - - Function *gc_statepoint_decl = Intrinsic::getDeclaration( - M, Intrinsic::experimental_gc_statepoint, argTypes); - // Then go ahead and use the builder do actually do the inserts. We insert // immediately before the previous instruction under the assumption that all // arguments will be available here. We can't insert afterwards since we may // be replacing a terminator. Instruction *insertBefore = CS.getInstruction(); IRBuilder<> Builder(insertBefore); - // First, create the statepoint (with all live ptrs as arguments). - std::vector args; - // target, #args, unused, args - Value *Target = CS.getCalledValue(); - args.push_back(Target); - int callArgSize = CS.arg_size(); - args.push_back( - ConstantInt::get(Type::getInt32Ty(M->getContext()), callArgSize)); - // TODO: add a 'Needs GC-rewrite' later flag - args.push_back(ConstantInt::get(Type::getInt32Ty(M->getContext()), 0)); - - // Copy all the arguments of the original call - args.insert(args.end(), CS.arg_begin(), CS.arg_end()); // Create the statepoint given all the arguments Instruction *token = nullptr; AttributeSet return_attributes; if (CS.isCall()) { CallInst *toReplace = cast(CS.getInstruction()); - CallInst *call = - Builder.CreateCall(gc_statepoint_decl, args, "safepoint_token"); - call->setTailCall(toReplace->isTailCall()); - call->setCallingConv(toReplace->getCallingConv()); + CallInst *Call = Builder.CreateGCStatepoint( + CS.getCalledValue(), makeArrayRef(CS.arg_begin(), CS.arg_end()), None, + None, "safepoint_token"); + Call->setTailCall(toReplace->isTailCall()); + Call->setCallingConv(toReplace->getCallingConv()); // Before we have to worry about GC semantics, all attributes are legal AttributeSet new_attrs = toReplace->getAttributes(); // In case if we can handle this set of sttributes - set up function attrs // directly on statepoint and return attrs later for gc_result intrinsic. - call->setAttributes(new_attrs.getFnAttributes()); + Call->setAttributes(new_attrs.getFnAttributes()); return_attributes = new_attrs.getRetAttributes(); // TODO: handle param attributes - token = call; + token = Call; // Put the following gc_result and gc_relocate calls immediately after the // the old call (which we're about to delete) @@ -903,6 +885,33 @@ Builder.SetCurrentDebugLocation(IP->getDebugLoc()); } else if (CS.isInvoke()) { + // TODO: make CreateGCStatepoint return an Instruction that we can cast to a + // Call or Invoke, instead of doing this junk here. + + // Fill in the one generic type'd argument (the function is also + // vararg) + std::vector argTypes; + argTypes.push_back(CS.getCalledValue()->getType()); + + Function *gc_statepoint_decl = Intrinsic::getDeclaration( + M, Intrinsic::experimental_gc_statepoint, argTypes); + + // First, create the statepoint (with all live ptrs as arguments). + std::vector args; + // target, #call args, unused, ... call parameters, #deopt args, ... deopt + // parameters, ... gc parameters + Value *Target = CS.getCalledValue(); + args.push_back(Target); + int callArgSize = CS.arg_size(); + // #call args + args.push_back(Builder.getInt32(callArgSize)); + // unused + args.push_back(Builder.getInt32(0)); + // call parameters + args.insert(args.end(), CS.arg_begin(), CS.arg_end()); + // #deopt args: 0 + args.push_back(Builder.getInt32(0)); + InvokeInst *toReplace = cast(CS.getInstruction()); // Insert the new invoke into the old block. We'll remove the old one in a @@ -938,30 +947,9 @@ // Only add the gc_result iff there is actually a used result if (!CS.getType()->isVoidTy() && !CS.getInstruction()->use_empty()) { - Instruction *gc_result = nullptr; - std::vector types; // one per 'any' type - types.push_back(CS.getType()); // result type - auto get_gc_result_id = [&](Type &Ty) { - if (Ty.isIntegerTy()) { - return Intrinsic::experimental_gc_result_int; - } else if (Ty.isFloatingPointTy()) { - return Intrinsic::experimental_gc_result_float; - } else if (Ty.isPointerTy()) { - return Intrinsic::experimental_gc_result_ptr; - } else { - llvm_unreachable("non java type encountered"); - } - }; - Intrinsic::ID Id = get_gc_result_id(*CS.getType()); - Value *gc_result_func = Intrinsic::getDeclaration(M, Id, types); - - std::vector args; - args.push_back(token); - gc_result = Builder.CreateCall( - gc_result_func, args, - CS.getInstruction()->hasName() ? CS.getInstruction()->getName() : ""); - - cast(gc_result)->setAttributes(return_attributes); + CallInst *gc_result = + Builder.CreateGCResult(token, CS.getType(), "gcresult"); + gc_result->setAttributes(return_attributes); return gc_result; } else { // No return value for the call. Index: test/Transforms/Safepoint/place.ll =================================================================== --- /dev/null +++ test/Transforms/Safepoint/place.ll @@ -0,0 +1,33 @@ +; RUN: opt %s -place-safepoints -S | FileCheck %s + +declare zeroext i1 @void_return_i1() +declare zeroext i1 @i1_return_i1(i1) + +define void @gc.safepoint_poll() { + ret void +} + +define i1 @test_basic() gc "statepoint-example" { +; CHECK-LABEL: test_basic +; This is checking that a statepoint_poll + statepoint + result is +; inserted for a function that doesn't take arguments. +; CHECK: gc.statepoint.p0f_isVoidf +; CHECK: gc.statepoint.p0f_i1f +; CHECK: gc.result.i1 +entry: + %call1 = tail call i1 ()* @void_return_i1() + ret i1 %call1 +} + +define i1 @test_argument() gc "statepoint-example" { +; CHECK-LABEL: test_argument +; This is checking that a statepoint_poll + statepoint + result is +; inserted for a function that takes 1 argument. +; CHECK: gc.statepoint.p0f_isVoidf +; CHECK: gc.statepoint.p0f_i1i1f +; CHECK: (i1 (i1)* @i1_return_i1, i32 1, i32 0, i1 false, i32 0) +; CHECK: gc.result.i1 +entry: + %call1 = tail call i1 (i1)* @i1_return_i1(i1 false) + ret i1 %call1 +} \ No newline at end of file