Start implementing support for @synchonized with the darwin ObjC API.
Patch by Fariborz!
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59377 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp
index 3ffdb4d..058278c 100644
--- a/lib/CodeGen/CGObjC.cpp
+++ b/lib/CodeGen/CGObjC.cpp
@@ -533,4 +533,10 @@
CGM.getObjCRuntime().EmitThrowStmt(*this, S);
}
+void CodeGenFunction::EmitObjCAtSynchronizedStmt(
+ const ObjCAtSynchronizedStmt &S)
+{
+ CGM.getObjCRuntime().EmitSynchronizedStmt(*this, S);
+}
+
CGObjCRuntime::~CGObjCRuntime() {}
diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp
index 4afbf3c..4726be2 100644
--- a/lib/CodeGen/CGObjCGNU.cpp
+++ b/lib/CodeGen/CGObjCGNU.cpp
@@ -128,6 +128,8 @@
const ObjCAtTryStmt &S);
virtual void EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtThrowStmt &S);
+ virtual void EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
+ const ObjCAtSynchronizedStmt &S);
};
} // end anonymous namespace
@@ -961,6 +963,11 @@
CGF.ErrorUnsupported(&S, "@throw statement");
}
+void CGObjCGNU::EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
+ const ObjCAtSynchronizedStmt &S) {
+ CGF.ErrorUnsupported(&S, "@synchronized statement");
+}
+
CodeGen::CGObjCRuntime *CodeGen::CreateGNUObjCRuntime(CodeGen::CodeGenModule &CGM){
return new CGObjCGNU(CGM);
}
diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp
index 70b9b49..296b1af 100644
--- a/lib/CodeGen/CGObjCMac.cpp
+++ b/lib/CodeGen/CGObjCMac.cpp
@@ -155,7 +155,13 @@
/// SetJmpFn - LLVM _setjmp function.
llvm::Function *SetJmpFn;
-
+
+ /// SyncEnterFn - LLVM object_sync_enter function.
+ llvm::Function *SyncEnterFn;
+
+ /// SyncExitFn - LLVM object_sync_exit function.
+ llvm::Function *SyncExitFn;
+
public:
ObjCTypesHelper(CodeGen::CodeGenModule &cgm);
~ObjCTypesHelper();
@@ -430,6 +436,8 @@
const ObjCAtTryStmt &S);
virtual void EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtThrowStmt &S);
+ virtual void EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
+ const ObjCAtSynchronizedStmt &S);
};
} // end anonymous namespace
@@ -1751,6 +1759,96 @@
EmitBranch(ExecuteTryExit ? E->FinallyBlock : E->FinallyNoExit);
}
+/// EmitSynchronizedStmt - Code gen for @synchronized(expr) stmt;
+/// Effectively generating code for:
+/// objc_sync_enter(expr);
+/// @try stmt @finally { objc_sync_exit(expr); }
+void CGObjCMac::EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
+ const ObjCAtSynchronizedStmt &S) {
+ // Create various blocks we refer to for handling @finally.
+ llvm::BasicBlock *FinallyBlock = CGF.createBasicBlock("finally");
+ llvm::BasicBlock *FinallyNoExit = CGF.createBasicBlock("finally.noexit");
+ llvm::BasicBlock *FinallyRethrow = CGF.createBasicBlock("finally.throw");
+ llvm::BasicBlock *FinallyEnd = CGF.createBasicBlock("finally.end");
+ llvm::Value *DestCode =
+ CGF.CreateTempAlloca(llvm::Type::Int32Ty, "finally.dst");
+
+ // Generate jump code. Done here so we can directly add things to
+ // the switch instruction.
+ llvm::BasicBlock *FinallyJump = CGF.createBasicBlock("finally.jump");
+ llvm::SwitchInst *FinallySwitch =
+ llvm::SwitchInst::Create(new llvm::LoadInst(DestCode, "", FinallyJump),
+ FinallyEnd, 10, FinallyJump);
+
+ // Push an EH context entry, used for handling rethrows and jumps
+ // through finally.
+ CodeGenFunction::ObjCEHEntry EHEntry(FinallyBlock, FinallyNoExit,
+ FinallySwitch, DestCode);
+ CGF.ObjCEHStack.push_back(&EHEntry);
+
+ // Allocate memory for the exception data and rethrow pointer.
+ llvm::Value *ExceptionData = CGF.CreateTempAlloca(ObjCTypes.ExceptionDataTy,
+ "exceptiondata.ptr");
+ llvm::Value *RethrowPtr = CGF.CreateTempAlloca(ObjCTypes.ObjectPtrTy,
+ "_rethrow");
+ // Call objc_sync_enter(sync.expr)
+ CGF.Builder.CreateCall(ObjCTypes.SyncEnterFn,
+ CGF.EmitScalarExpr(S.getSynchExpr()));
+
+ // Enter a new try block and call setjmp.
+ CGF.Builder.CreateCall(ObjCTypes.ExceptionTryEnterFn, ExceptionData);
+ llvm::Value *JmpBufPtr = CGF.Builder.CreateStructGEP(ExceptionData, 0,
+ "jmpbufarray");
+ JmpBufPtr = CGF.Builder.CreateStructGEP(JmpBufPtr, 0, "tmp");
+ llvm::Value *SetJmpResult = CGF.Builder.CreateCall(ObjCTypes.SetJmpFn,
+ JmpBufPtr, "result");
+
+ llvm::BasicBlock *TryBlock = CGF.createBasicBlock("try");
+ llvm::BasicBlock *TryHandler = CGF.createBasicBlock("try.handler");
+ CGF.Builder.CreateCondBr(CGF.Builder.CreateIsNotNull(SetJmpResult, "threw"),
+ TryHandler, TryBlock);
+
+ // Emit the @try block.
+ CGF.EmitBlock(TryBlock);
+ CGF.EmitStmt(S.getSynchBody());
+ CGF.EmitJumpThroughFinally(&EHEntry, FinallyEnd);
+
+ // Emit the "exception in @try" block.
+ CGF.EmitBlock(TryHandler);
+
+ // Retrieve the exception object. We may emit multiple blocks but
+ // nothing can cross this so the value is already in SSA form.
+ llvm::Value *Caught = CGF.Builder.CreateCall(ObjCTypes.ExceptionExtractFn,
+ ExceptionData,
+ "caught");
+ EHEntry.Exception = Caught;
+ CGF.Builder.CreateStore(Caught, RethrowPtr);
+ CGF.EmitJumpThroughFinally(&EHEntry, FinallyRethrow, false);
+
+ // Pop the exception-handling stack entry. It is important to do
+ // this now, because the code in the @finally block is not in this
+ // context.
+ CGF.ObjCEHStack.pop_back();
+
+ // Emit the @finally block.
+ CGF.EmitBlock(FinallyBlock);
+ CGF.Builder.CreateCall(ObjCTypes.ExceptionTryExitFn, ExceptionData);
+
+ CGF.EmitBlock(FinallyNoExit);
+ // objc_sync_exit(expr); As finally's sole statement.
+ CGF.Builder.CreateCall(ObjCTypes.SyncExitFn,
+ CGF.EmitScalarExpr(S.getSynchExpr()));
+
+ CGF.EmitBlock(FinallyJump);
+
+ CGF.EmitBlock(FinallyRethrow);
+ CGF.Builder.CreateCall(ObjCTypes.ExceptionThrowFn,
+ CGF.Builder.CreateLoad(RethrowPtr));
+ CGF.Builder.CreateUnreachable();
+
+ CGF.EmitBlock(FinallyEnd);
+}
+
/* *** Private Interface *** */
/// EmitImageInfo - Emit the image info marker used to encode some module
@@ -2415,6 +2513,23 @@
Params,
false),
"objc_exception_match");
+
+ // synchronized APIs
+ // void objc_sync_enter (id)
+ Params.clear();
+ Params.push_back(ObjectPtrTy);
+ SyncEnterFn =
+ CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::VoidTy,
+ Params,
+ false),
+ "objc_sync_enter");
+ // void objc_sync_exit (id)
+ SyncExitFn =
+ CGM.CreateRuntimeFunction(llvm::FunctionType::get(llvm::Type::VoidTy,
+ Params,
+ false),
+ "objc_sync_exit");
+
Params.clear();
Params.push_back(llvm::PointerType::getUnqual(llvm::Type::Int32Ty));
diff --git a/lib/CodeGen/CGObjCRuntime.h b/lib/CodeGen/CGObjCRuntime.h
index d690289..435e148 100644
--- a/lib/CodeGen/CGObjCRuntime.h
+++ b/lib/CodeGen/CGObjCRuntime.h
@@ -38,6 +38,7 @@
class ObjCAtTryStmt;
class ObjCAtThrowStmt;
+ class ObjCAtSynchronizedStmt;
class ObjCCategoryImplDecl;
class ObjCImplementationDecl;
class ObjCInterfaceDecl;
@@ -142,6 +143,8 @@
const ObjCAtTryStmt &S) = 0;
virtual void EmitThrowStmt(CodeGen::CodeGenFunction &CGF,
const ObjCAtThrowStmt &S) = 0;
+ virtual void EmitSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
+ const ObjCAtSynchronizedStmt &S) = 0;
};
/// Creates an instance of an Objective-C runtime class.
diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp
index 767e275..3f2cd80 100644
--- a/lib/CodeGen/CGStmt.cpp
+++ b/lib/CodeGen/CGStmt.cpp
@@ -92,7 +92,7 @@
EmitObjCAtThrowStmt(cast<ObjCAtThrowStmt>(*S));
break;
case Stmt::ObjCAtSynchronizedStmtClass:
- ErrorUnsupported(S, "@synchronized statement");
+ EmitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(*S));
break;
case Stmt::ObjCForCollectionStmtClass:
EmitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(*S));
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index 50b35bd..5d99dba 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -401,6 +401,7 @@
void EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S);
void EmitObjCAtTryStmt(const ObjCAtTryStmt &S);
void EmitObjCAtThrowStmt(const ObjCAtThrowStmt &S);
+ void EmitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt &S);
//===--------------------------------------------------------------------===//
// LValue Expression Emission