ABISignature::ABISignature(const ReaderCallSignature &Signature, GenIR &Reader, const ABIInfo &TheABIInfo) { const CallArgType &ResultType = Signature.getResultType(); const std::vector<CallArgType> &ArgTypes = Signature.getArgumentTypes(); const uint32_t NumArgs = ArgTypes.size(); ABIType ABIResultType(Reader.getType(ResultType.CorType, ResultType.Class), GenIR::isSignedIntegralType(ResultType.CorType)); SmallVector<ABIType, 16> ABIArgTypes(NumArgs); uint32_t I = 0; for (const CallArgType &Arg : ArgTypes) { ABIArgTypes[I++] = ABIType(Reader.getType(Arg.CorType, Arg.Class), GenIR::isSignedIntegralType(Arg.CorType)); } bool IsManagedCallingConv = false; CallingConv::ID CC = getLLVMCallingConv( getNormalizedCallingConvention(Signature), IsManagedCallingConv); TheABIInfo.computeSignatureInfo(CC, IsManagedCallingConv, ABIResultType, ABIArgTypes, Result, Args); if (Result.getKind() == ABIArgInfo::Indirect) { FuncResultType = Reader.getManagedPointerType(Result.getType()); } else { FuncResultType = Result.getType(); } }
void ABISignature::expand(GenIR &Reader, ArrayRef<ABIArgInfo::Expansion> Expansions, Value *Source, MutableArrayRef<Value *> Values, MutableArrayRef<Type *> Types, bool IsResult) { assert(Source != nullptr); assert(Source->getType()->isPointerTy()); assert(Reader.doesValueRepresentStruct(Source)); assert(Expansions.size() > 0); assert((IsResult && Values.size() == 1) || (Values.size() == Expansions.size())); LLVMContext &LLVMContext = *Reader.JitContext->LLVMContext; IRBuilder<> &Builder = *Reader.LLVMBuilder; Type *ResultType = nullptr; Value *ResultValue = nullptr; if (IsResult) { ResultType = getExpandedResultType(LLVMContext, Expansions); ResultValue = Constant::getNullValue(ResultType); } Type *BytePtrTy = Type::getInt8PtrTy(LLVMContext, 0); Value *SourcePtr = Builder.CreatePointerCast(Source, BytePtrTy); for (int32_t I = 0; I < static_cast<int32_t>(Expansions.size()); I++) { const ABIArgInfo::Expansion &Exp = Expansions[I]; Value *LoadPtr = Builder.CreateConstGEP1_32(SourcePtr, Exp.Offset); LoadPtr = Builder.CreatePointerCast(LoadPtr, Exp.TheType->getPointerTo(0)); const bool IsVolatile = false; Value *Value = Builder.CreateLoad(LoadPtr, IsVolatile); if (IsResult) { ResultValue = Builder.CreateInsertValue(ResultValue, Value, I); } else { Values[I] = Value; } if (Types.size() > 0) { Types[I] = Exp.TheType; } } if (IsResult) { Values[0] = ResultValue; } }
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); if (AddrOfCaptureThreadGlobal != nullptr) { Value *RawThreadTrapAddress = ConstantInt::get( LLVMContext, APInt(Reader.TargetPointerSizeInBits, (uint64_t)AddrOfCaptureThreadGlobal)); ThreadTrapAddress = Builder.CreateIntToPtr(RawThreadTrapAddress, ThreadTrapAddressTy); } else { Value *IndirectThreadTrapAddress = ConstantInt::get( LLVMContext, APInt(Reader.TargetPointerSizeInBits, (uint64_t)IndirectAddrOfCaptureThreadGlobal)); Type *IndirectAddressTy = Reader.getUnmanagedPointerType(ThreadTrapAddressTy); Value *TypedIndirectAddress = Builder.CreateIntToPtr(IndirectThreadTrapAddress, IndirectAddressTy); ThreadTrapAddress = Builder.CreateLoad(TypedIndirectAddress); } // 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; }