Support for synchronized methods.

Add calls to MONITOR_ENTER on entry to the method and MONITOR_EXIT on returns.

We can't catch exceptions yet; when we can we will need to add a try/fault for the entire method
so that we can call MONITOR_EXIT on unhandled exceptions.

We have a synchronized method in HelloWorld (System.IO.TextWriter+SyncTextWriter.WriteLine).
I verified that the generated IR looks correct. I also tested with a simple two-thread race that the
behavior of synchronized instance and static methods is as expected.
This commit is contained in:
Eugene Rozenfeld 2015-04-17 13:44:16 -07:00
Родитель 6cce44d449
Коммит ac3078375b
3 изменённых файлов: 92 добавлений и 35 удалений

Просмотреть файл

@ -2839,7 +2839,7 @@ public:
virtual IRNode *refAnyType(IRNode *Arg1) = 0;
virtual IRNode *refAnyVal(IRNode *Val, CORINFO_RESOLVED_TOKEN *ResolvedToken);
virtual void rethrow() = 0;
virtual void returnOpcode(IRNode *Opr, bool IsSynchronousMethod) = 0;
virtual void returnOpcode(IRNode *Opr, bool IsSynchronizedMethod) = 0;
virtual IRNode *shift(ReaderBaseNS::ShiftOpcode Opcode, IRNode *ShiftAmount,
IRNode *ShiftOperand) = 0;
virtual IRNode *sizeofOpcode(CORINFO_RESOLVED_TOKEN *ResolvedToken) = 0;

Просмотреть файл

@ -436,7 +436,7 @@ public:
};
void rethrow() override { throw NotYetImplementedException("rethrow"); };
void returnOpcode(IRNode *Opr, bool SynchronousMethod) override;
void returnOpcode(IRNode *Opr, bool IsSynchronizedMethod) override;
IRNode *shift(ReaderBaseNS::ShiftOpcode Opcode, IRNode *ShiftAmount,
IRNode *ShiftOperand) override;
IRNode *sizeofOpcode(CORINFO_RESOLVED_TOKEN *ResolvedToken) override;
@ -755,6 +755,13 @@ public:
IRNode *callRuntimeHandleHelper(CorInfoHelpFunc Helper, IRNode *Arg1,
IRNode *Arg2, IRNode *NullCheckArg);
/// Generate a helper call to enter or exit a monitor used by synchronized
/// methods.
///
/// \param IsEnter true if the monitor should be entered; false if the monitor
/// should be exited.
void callMonitorHelper(bool IsEnter);
IRNode *convertToBoxHelperArgumentType(IRNode *Opr,
CorInfoType CorType) override;
@ -1274,6 +1281,13 @@ private:
bool NeedsSecurityObject;
llvm::BasicBlock *EntryBlock;
llvm::Instruction *TempInsertionPoint;
IRNode *MethodSyncHandle; ///< If the method is synchronized, this is
///< the handle used for entering and exiting
///< the monitor.
llvm::Value *SyncFlag; ///< For synchronized methods this flag
///< indicates whether the monitor has been
///< entered. It is set and checked by monitor
///< helpers.
uint32_t TargetPointerSizeInBits;
const uint32_t UnmanagedAddressSpace = 0;
const uint32_t ManagedAddressSpace = 1;

Просмотреть файл

@ -303,9 +303,10 @@ void GenIR::readerPrePass(uint8_t *Buffer, uint32_t NumBytes) {
ABIMethodSignature(MethodSignature, *this, *JitContext->TheABIInfo);
Function = ABIMethodSig.createFunction(*this, *JitContext->CurrentModule);
EntryBlock = BasicBlock::Create(*JitContext->LLVMContext, "entry", Function);
llvm::LLVMContext &LLVMContext = *JitContext->LLVMContext;
EntryBlock = BasicBlock::Create(LLVMContext, "entry", Function);
LLVMBuilder = new IRBuilder<>(*this->JitContext->LLVMContext);
LLVMBuilder = new IRBuilder<>(LLVMContext);
LLVMBuilder->SetInsertPoint(EntryBlock);
// Note numArgs may exceed the IL argument count when there
@ -367,9 +368,26 @@ void GenIR::readerPrePass(uint8_t *Buffer, uint32_t NumBytes) {
// Check for special cases where the Jit needs to do extra work.
const uint32_t MethodFlags = getCurrentMethodAttribs();
// TODO: support for synchronized methods
// Check for synchronized method. If a method is synchronized the JIT is
// required to insert a helper call to MONITOR_ENTER before we start the user
// code. A similar call to MONITOR_EXIT will be placed at the return site.
// TODO: when we start catching exceptions we should create a try/fault for
// the entire method so that we can exit the monitor on unhandled exceptions.
MethodSyncHandle = nullptr;
SyncFlag = nullptr;
if (MethodFlags & CORINFO_FLG_SYNCH) {
throw NotYetImplementedException("synchronized method");
MethodSyncHandle = rdrGetCritSect();
Type *SyncFlagType = Type::getInt8Ty(LLVMContext);
// Create address taken local SyncFlag. This will be passed by address to
// MONITOR_ENTER and MONITOR_EXIT. MONITOR_ENTER will set SyncFlag once lock
// has been obtained, MONITOR_EXIT only releases lock if SyncFlag is set.
SyncFlag = createTemporary(SyncFlagType, "SyncFlag");
Constant *ZeroConst = Constant::getNullValue(SyncFlagType);
LLVMBuilder->CreateStore(ZeroConst, SyncFlag);
const bool IsEnter = true;
callMonitorHelper(IsEnter);
}
if ((JitFlags & CORJIT_FLG_DEBUG_CODE) && !(JitFlags & CORJIT_FLG_IL_STUB)) {
@ -539,6 +557,19 @@ void GenIR::insertIRForSecurityObject() {
// via the GC encoding.
}
void GenIR::callMonitorHelper(bool IsEnter) {
CorInfoHelpFunc HelperId;
const uint32_t MethodFlags = getCurrentMethodAttribs();
if (MethodFlags & CORINFO_FLG_STATIC) {
HelperId =
IsEnter ? CORINFO_HELP_MON_ENTER_STATIC : CORINFO_HELP_MON_EXIT_STATIC;
} else {
HelperId = IsEnter ? CORINFO_HELP_MON_ENTER : CORINFO_HELP_MON_EXIT;
}
callHelperImpl(HelperId, Type::getVoidTy(*JitContext->LLVMContext),
MethodSyncHandle, (IRNode *)SyncFlag);
}
#pragma endregion
#pragma region UTILITIES
@ -4236,44 +4267,56 @@ bool GenIR::fgOptRecurse(mdToken Token) {
return true;
}
void GenIR::returnOpcode(IRNode *Opr, bool IsSynchronousMethod) {
void GenIR::returnOpcode(IRNode *Opr, bool IsSynchronizedMethod) {
const ABIArgInfo &ResultInfo = ABIMethodSig.getResultInfo();
const CallArgType &ResultArgType = MethodSignature.getResultType();
CorInfoType ResultCorType = ResultArgType.CorType;
Value *ResultValue = nullptr;
bool IsVoidReturn = ResultCorType == CORINFO_TYPE_VOID;
if (ResultCorType == CORINFO_TYPE_VOID) {
if (IsVoidReturn) {
assert(Opr == nullptr);
assert(ResultInfo.getType()->isVoidTy());
LLVMBuilder->CreateRetVoid();
return;
}
assert(Opr != nullptr);
Value *ResultValue = nullptr;
if (ResultInfo.getKind() == ABIArgInfo::Indirect) {
Type *ResultTy = ResultInfo.getType();
ResultValue = convertFromStackType(Opr, ResultCorType, ResultTy);
CORINFO_CLASS_HANDLE ResultClass = ResultArgType.Class;
if (JitContext->JitInfo->isStructRequiringStackAllocRetBuf(ResultClass) ==
TRUE) {
// The return buffer must be on the stack; a simple store will suffice.
LLVMBuilder->CreateStore(ResultValue, IndirectResult);
} else {
const bool IsVolatile = false;
storeIndirectArg(ResultArgType, ResultValue, IndirectResult, IsVolatile);
}
ResultValue = IndirectResult;
} else {
Type *ResultTy = getType(ResultCorType, ResultArgType.Class);
ResultValue = convertFromStackType(Opr, ResultCorType, ResultTy);
ResultValue =
ABISignature::coerce(*this, ResultInfo.getType(), ResultValue);
assert(Opr != nullptr);
if (ResultInfo.getKind() == ABIArgInfo::Indirect) {
Type *ResultTy = ResultInfo.getType();
ResultValue = convertFromStackType(Opr, ResultCorType, ResultTy);
CORINFO_CLASS_HANDLE ResultClass = ResultArgType.Class;
if (JitContext->JitInfo->isStructRequiringStackAllocRetBuf(ResultClass) ==
TRUE) {
// The return buffer must be on the stack; a simple store will suffice.
LLVMBuilder->CreateStore(ResultValue, IndirectResult);
} else {
const bool IsVolatile = false;
storeIndirectArg(ResultArgType, ResultValue, IndirectResult,
IsVolatile);
}
ResultValue = IndirectResult;
} else {
Type *ResultTy = getType(ResultCorType, ResultArgType.Class);
ResultValue = convertFromStackType(Opr, ResultCorType, ResultTy);
ResultValue =
ABISignature::coerce(*this, ResultInfo.getType(), ResultValue);
}
}
LLVMBuilder->CreateRet(ResultValue);
// If the method is synchronized, then we must insert a call to MONITOR_EXIT
// before returning. The call to MONITOR_EXIT must occur after the return
// value has been calculated.
if (IsSynchronizedMethod) {
const bool IsEnter = false;
callMonitorHelper(IsEnter);
}
if (IsVoidReturn) {
LLVMBuilder->CreateRetVoid();
} else {
LLVMBuilder->CreateRet(ResultValue);
}
}
bool GenIR::needSequencePoints() { return false; }