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