CallSite ABICallSignature::emitUnmanagedCall(GenIR &Reader, Value *Target, bool MayThrow, ArrayRef<Value *> Arguments, Value *&Result) const { const LLILCJitContext &JitContext = *Reader.JitContext; const struct CORINFO_EE_INFO::InlinedCallFrameInfo &CallFrameInfo = JitContext.EEInfo.inlinedCallFrameInfo; LLVMContext &LLVMContext = *JitContext.LLVMContext; Type *Int8Ty = Type::getInt8Ty(LLVMContext); Type *Int32Ty = Type::getInt32Ty(LLVMContext); Type *Int64Ty = Type::getInt64Ty(LLVMContext); Type *Int8PtrTy = Reader.getUnmanagedPointerType(Int8Ty); IRBuilder<> &Builder = *Reader.LLVMBuilder; Reader.insertIRForUnmanagedCallFrame(); Value *CallFrame = Reader.UnmanagedCallFrame; Value *Thread = Reader.ThreadPointer; assert(CallFrame != nullptr); assert(Thread != nullptr); // Set m_pDatum if necessary // // TODO: this needs to be updated for direct unmanaged calls, which require // the target method handle instead of the stub secret parameter. if (Reader.MethodSignature.hasSecretParameter()) { Value *SecretParameter = Reader.secretParam(); Value *CallTargetAddress = getFieldAddress(Builder, CallFrame, CallFrameInfo.offsetOfCallTarget, SecretParameter->getType()); Builder.CreateStore(SecretParameter, CallTargetAddress); } // Push the unmanaged call frame Value *FrameVPtr = getFieldAddress(Builder, CallFrame, CallFrameInfo.offsetOfFrameVptr, Int8Ty); Value *ThreadBase = Builder.CreateLoad(Thread); Value *ThreadFrameAddress = getFieldAddress( Builder, ThreadBase, JitContext.EEInfo.offsetOfThreadFrame, Int8PtrTy); Builder.CreateStore(FrameVPtr, ThreadFrameAddress); // Compute the address of the return address field Value *ReturnAddressAddress = getFieldAddress( Builder, CallFrame, CallFrameInfo.offsetOfReturnAddress, Int8PtrTy); // Compute the address of the GC mode field Value *GCStateAddress = getFieldAddress( Builder, ThreadBase, JitContext.EEInfo.offsetOfGCState, Int8Ty); // Compute address of the thread trap field Value *ThreadTrapAddress = nullptr; Type *ThreadTrapAddressTy = Reader.getUnmanagedPointerType(Int32Ty); void *IndirectAddrOfCaptureThreadGlobal = nullptr; void *AddrOfCaptureThreadGlobal = (void *)JitContext.JitInfo->getAddrOfCaptureThreadGlobal( &IndirectAddrOfCaptureThreadGlobal); void *AddrOfCaptureThreadHandle; bool IsIndirect; const bool IsReadOnly = true; const bool IsRelocatable = true; const bool IsCallTarget = false; if (AddrOfCaptureThreadGlobal != nullptr) { AddrOfCaptureThreadHandle = AddrOfCaptureThreadGlobal; IsIndirect = false; } else { AddrOfCaptureThreadHandle = IndirectAddrOfCaptureThreadGlobal; IsIndirect = true; } Value *RawThreadTrapAddress = Reader.handleToIRNode(mdtCaptureThreadGlobal, AddrOfCaptureThreadHandle, AddrOfCaptureThreadHandle, IsIndirect, IsReadOnly, IsRelocatable, IsCallTarget); ThreadTrapAddress = Builder.CreateIntToPtr(RawThreadTrapAddress, ThreadTrapAddressTy); // Compute address of GC pause helper Value *PauseHelperAddress = (Value *)Reader.getHelperCallAddress(CORINFO_HELP_STOP_FOR_GC); // Construct the call. // // The signature of the intrinsic is: // @llvm.experimental_gc_transition( // fn_ptr target, // i32 numCallArgs, // i32 flags, // ... call args ..., // i32 numTransitionArgs, // ... transition args..., // i32 numDeoptArgs, // ... deopt args...) // // In the case of CoreCLR, there are 4 transition args and 0 deopt args. // // The transition args are: // 0) Address of the return address field // 1) Address of the GC mode field // 2) Address of the thread trap global // 3) Address of CORINFO_HELP_STOP_FOR_GC Module *M = Reader.Function->getParent(); Type *CallTypeArgs[] = {Target->getType()}; Function *CallIntrinsic = Intrinsic::getDeclaration( M, Intrinsic::experimental_gc_statepoint, CallTypeArgs); const uint32_t PrefixArgCount = 5; const uint32_t TransitionArgCount = 4; const uint32_t PostfixArgCount = TransitionArgCount + 2; const uint32_t TargetArgCount = Arguments.size(); SmallVector<Value *, 24> IntrinsicArgs(PrefixArgCount + TargetArgCount + PostfixArgCount); // ID, nop bytes, call target and target arguments IntrinsicArgs[0] = ConstantInt::get(Int64Ty, 0); IntrinsicArgs[1] = ConstantInt::get(Int32Ty, 0); IntrinsicArgs[2] = Target; IntrinsicArgs[3] = ConstantInt::get(Int32Ty, TargetArgCount); IntrinsicArgs[4] = ConstantInt::get(Int32Ty, (uint32_t)StatepointFlags::GCTransition); uint32_t I, J; for (I = 0, J = PrefixArgCount; I < TargetArgCount; I++, J++) { IntrinsicArgs[J] = Arguments[I]; } // GC transition arguments IntrinsicArgs[J] = ConstantInt::get(Int32Ty, TransitionArgCount); IntrinsicArgs[J + 1] = ReturnAddressAddress; IntrinsicArgs[J + 2] = GCStateAddress; IntrinsicArgs[J + 3] = ThreadTrapAddress; IntrinsicArgs[J + 4] = PauseHelperAddress; // Deopt arguments IntrinsicArgs[J + 5] = ConstantInt::get(Int32Ty, 0); CallSite Call = Reader.makeCall(CallIntrinsic, MayThrow, IntrinsicArgs); // Get the call result if necessary if (!FuncResultType->isVoidTy()) { Type *ResultTypeArgs[] = {FuncResultType}; Function *ResultIntrinsic = Intrinsic::getDeclaration( M, Intrinsic::experimental_gc_result, ResultTypeArgs); Result = Builder.CreateCall(ResultIntrinsic, Call.getInstruction()); } // Deactivate the unmanaged call frame Builder.CreateStore(Constant::getNullValue(Int8PtrTy), ReturnAddressAddress); // Pop the unmanaged call frame Value *FrameLinkAddress = getFieldAddress( Builder, CallFrame, CallFrameInfo.offsetOfFrameLink, Int8PtrTy); Value *FrameLink = Builder.CreateLoad(FrameLinkAddress); Builder.CreateStore(FrameLink, ThreadFrameAddress); return Call; }
CallSite ABICallSignature::emitUnmanagedCall(GenIR &Reader, Value *Target, bool MayThrow, ArrayRef<Value *> Arguments) const { const LLILCJitContext &JitContext = *Reader.JitContext; const struct CORINFO_EE_INFO::InlinedCallFrameInfo &CallFrameInfo = JitContext.EEInfo.inlinedCallFrameInfo; LLVMContext &LLVMContext = *JitContext.LLVMContext; Type *Int8Ty = Type::getInt8Ty(LLVMContext); Type *Int32Ty = Type::getInt32Ty(LLVMContext); Type *Int8PtrTy = Reader.getUnmanagedPointerType(Int8Ty); IRBuilder<> &Builder = *Reader.LLVMBuilder; Reader.insertIRForUnmanagedCallFrame(); Value *CallFrame = Reader.UnmanagedCallFrame; Value *Thread = Reader.ThreadPointer; assert(CallFrame != nullptr); assert(Thread != nullptr); // Set m_pDatum if necessary // // TODO: this needs to be updated for direct unmanaged calls, which require // the target method handle instead of the stub secret parameter. if (Reader.MethodSignature.hasSecretParameter()) { Value *SecretParameter = Reader.secretParam(); Value *CallTargetAddress = getFieldAddress(Builder, CallFrame, CallFrameInfo.offsetOfCallTarget, SecretParameter->getType()); Builder.CreateStore(SecretParameter, CallTargetAddress); } // Push the unmanaged call frame Value *FrameVPtr = getFieldAddress(Builder, CallFrame, CallFrameInfo.offsetOfFrameVptr, Int8Ty); Value *ThreadBase = Builder.CreateLoad(Thread); Value *ThreadFrameAddress = getFieldAddress( Builder, ThreadBase, JitContext.EEInfo.offsetOfThreadFrame, Int8PtrTy); Builder.CreateStore(FrameVPtr, ThreadFrameAddress); // Compute the address of the return address field Value *ReturnAddressAddress = getFieldAddress( Builder, CallFrame, CallFrameInfo.offsetOfReturnAddress, Int8PtrTy); // Compute the address of the GC mode field Value *GCStateAddress = getFieldAddress( Builder, ThreadBase, JitContext.EEInfo.offsetOfGCState, Int8Ty); // Compute address of the thread trap field Value *ThreadTrapAddress = nullptr; Type *ThreadTrapAddressTy = Reader.getUnmanagedPointerType(Int32Ty); void *IndirectAddrOfCaptureThreadGlobal = nullptr; void *AddrOfCaptureThreadGlobal = (void *)JitContext.JitInfo->getAddrOfCaptureThreadGlobal( &IndirectAddrOfCaptureThreadGlobal); void *AddrOfCaptureThreadHandle; bool IsIndirect; const bool IsReadOnly = true; const bool IsRelocatable = true; const bool IsCallTarget = false; if (AddrOfCaptureThreadGlobal != nullptr) { AddrOfCaptureThreadHandle = AddrOfCaptureThreadGlobal; IsIndirect = false; } else { AddrOfCaptureThreadHandle = IndirectAddrOfCaptureThreadGlobal; IsIndirect = true; } Value *RawThreadTrapAddress = Reader.handleToIRNode(mdtCaptureThreadGlobal, AddrOfCaptureThreadHandle, AddrOfCaptureThreadHandle, IsIndirect, IsReadOnly, IsRelocatable, IsCallTarget); ThreadTrapAddress = Builder.CreateIntToPtr(RawThreadTrapAddress, ThreadTrapAddressTy); // Compute address of GC pause helper Value *PauseHelperAddress = (Value *)Reader.getHelperCallAddress(CORINFO_HELP_STOP_FOR_GC); // Construct the call. // // The transition args are: // 0) Address of the return address field // 1) Address of the GC mode field // 2) Address of the thread trap global // 3) Address of CORINFO_HELP_STOP_FOR_GC Value *TransitionArgs[] = {ReturnAddressAddress, GCStateAddress, ThreadTrapAddress, PauseHelperAddress}; OperandBundleDef TransitionBundle("gc-transition", TransitionArgs); CallSite Call = Reader.makeCall(Target, MayThrow, Arguments, {TransitionBundle}); assert(Call.getOperandBundle(LLVMContext::OB_gc_transition).hasValue() && "tag string mismatch?"); // Deactivate the unmanaged call frame Builder.CreateStore(Constant::getNullValue(Int8PtrTy), ReturnAddressAddress); // Pop the unmanaged call frame Value *FrameLinkAddress = getFieldAddress( Builder, CallFrame, CallFrameInfo.offsetOfFrameLink, Int8PtrTy); Value *FrameLink = Builder.CreateLoad(FrameLinkAddress); Builder.CreateStore(FrameLink, ThreadFrameAddress); return Call; }