Fix two @synchronized bugs found by inspection: the expression to sychronize on should only be evaluated once, and it is evaluated outside the cleanup scope.

Also, lift SyncEnter and SyncExit up in nervous anticipation of x86-64
zero cost EH.

llvm-svn: 65362
This commit is contained in:
Daniel Dunbar 2009-02-24 01:43:46 +00:00
parent d6e190d899
commit 94ceb61574
2 changed files with 48 additions and 43 deletions

View File

@ -105,6 +105,12 @@ public:
/// ExceptionThrowFn - LLVM objc_exception_throw function. /// ExceptionThrowFn - LLVM objc_exception_throw function.
llvm::Function *ExceptionThrowFn; llvm::Function *ExceptionThrowFn;
/// SyncEnterFn - LLVM object_sync_enter function.
llvm::Function *SyncEnterFn;
/// SyncExitFn - LLVM object_sync_exit function.
llvm::Function *SyncExitFn;
ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm); ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm);
~ObjCCommonTypesHelper(){} ~ObjCCommonTypesHelper(){}
}; };
@ -188,12 +194,6 @@ public:
/// SetJmpFn - LLVM _setjmp function. /// SetJmpFn - LLVM _setjmp function.
llvm::Function *SetJmpFn; llvm::Function *SetJmpFn;
/// SyncEnterFn - LLVM object_sync_enter function.
llvm::Function *SyncEnterFn;
/// SyncExitFn - LLVM object_sync_exit function.
llvm::Function *SyncExitFn;
public: public:
ObjCTypesHelper(CodeGen::CodeGenModule &cgm); ObjCTypesHelper(CodeGen::CodeGenModule &cgm);
~ObjCTypesHelper() {} ~ObjCTypesHelper() {}
@ -1892,6 +1892,18 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
llvm::BasicBlock *FinallyNoExit = CGF.createBasicBlock("finally.noexit"); llvm::BasicBlock *FinallyNoExit = CGF.createBasicBlock("finally.noexit");
llvm::BasicBlock *FinallyRethrow = CGF.createBasicBlock("finally.throw"); llvm::BasicBlock *FinallyRethrow = CGF.createBasicBlock("finally.throw");
llvm::BasicBlock *FinallyEnd = CGF.createBasicBlock("finally.end"); llvm::BasicBlock *FinallyEnd = CGF.createBasicBlock("finally.end");
// For @synchronized, call objc_sync_enter(sync.expr). The
// evaluation of the expression must occur before we enter the
// @synchronized. We can safely avoid a temp here because jumps into
// @synchronized are illegal & this will dominate uses.
llvm::Value *SyncArg = 0;
if (!isTry) {
SyncArg =
CGF.EmitScalarExpr(cast<ObjCAtSynchronizedStmt>(S).getSynchExpr());
SyncArg = CGF.Builder.CreateBitCast(SyncArg, ObjCTypes.ObjectPtrTy);
CGF.Builder.CreateCall(ObjCTypes.SyncEnterFn, SyncArg);
}
// Push an EH context entry, used for handling rethrows and jumps // Push an EH context entry, used for handling rethrows and jumps
// through finally. // through finally.
@ -1908,14 +1920,6 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
"_call_try_exit"); "_call_try_exit");
CGF.Builder.CreateStore(llvm::ConstantInt::getTrue(), CallTryExitPtr); CGF.Builder.CreateStore(llvm::ConstantInt::getTrue(), CallTryExitPtr);
if (!isTry) {
// For @synchronized, call objc_sync_enter(sync.expr)
llvm::Value *Arg = CGF.EmitScalarExpr(
cast<ObjCAtSynchronizedStmt>(S).getSynchExpr());
Arg = CGF.Builder.CreateBitCast(Arg, ObjCTypes.ObjectPtrTy);
CGF.Builder.CreateCall(ObjCTypes.SyncEnterFn, Arg);
}
// Enter a new try block and call setjmp. // Enter a new try block and call setjmp.
CGF.Builder.CreateCall(ObjCTypes.ExceptionTryEnterFn, ExceptionData); CGF.Builder.CreateCall(ObjCTypes.ExceptionTryEnterFn, ExceptionData);
llvm::Value *JmpBufPtr = CGF.Builder.CreateStructGEP(ExceptionData, 0, llvm::Value *JmpBufPtr = CGF.Builder.CreateStructGEP(ExceptionData, 0,
@ -2079,14 +2083,10 @@ void CGObjCMac::EmitTryOrSynchronizedStmt(CodeGen::CodeGenFunction &CGF,
if (const ObjCAtFinallyStmt* FinallyStmt = if (const ObjCAtFinallyStmt* FinallyStmt =
cast<ObjCAtTryStmt>(S).getFinallyStmt()) cast<ObjCAtTryStmt>(S).getFinallyStmt())
CGF.EmitStmt(FinallyStmt->getFinallyBody()); CGF.EmitStmt(FinallyStmt->getFinallyBody());
} } else {
else { // Emit objc_sync_exit(expr); as finally's sole statement for
// For @synchronized objc_sync_exit(expr); As finally's sole statement. // @synchronized.
// For @synchronized, call objc_sync_enter(sync.expr) CGF.Builder.CreateCall(ObjCTypes.SyncExitFn, SyncArg);
llvm::Value *Arg = CGF.EmitScalarExpr(
cast<ObjCAtSynchronizedStmt>(S).getSynchExpr());
Arg = CGF.Builder.CreateBitCast(Arg, ObjCTypes.ObjectPtrTy);
CGF.Builder.CreateCall(ObjCTypes.SyncExitFn, Arg);
} }
// Emit the switch block // Emit the switch block
@ -2740,9 +2740,18 @@ ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm)
Params.push_back(IdType); Params.push_back(IdType);
FTy = Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params), false); FTy = Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params), false);
ExceptionThrowFn = ExceptionThrowFn =
CGM.CreateRuntimeFunction(FTy, "objc_exception_throw"); CGM.CreateRuntimeFunction(FTy, "objc_exception_throw");
// synchronized APIs
// void objc_sync_enter (id)
// void objc_sync_exit (id)
Params.clear();
Params.push_back(IdType);
FTy = Types.GetFunctionType(Types.getFunctionInfo(Ctx.VoidTy, Params), false);
SyncEnterFn = CGM.CreateRuntimeFunction(FTy, "objc_sync_enter");
SyncExitFn = CGM.CreateRuntimeFunction(FTy, "objc_sync_exit");
} }
ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm)
@ -3049,23 +3058,6 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm)
false), false),
"objc_exception_match"); "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.clear();
Params.push_back(llvm::PointerType::getUnqual(llvm::Type::Int32Ty)); Params.push_back(llvm::PointerType::getUnqual(llvm::Type::Int32Ty));
SetJmpFn = SetJmpFn =

View File

@ -1,5 +1,6 @@
// RUN: clang -emit-llvm -triple=i686-apple-darwin8 -o %t %s // RUN: clang -emit-llvm -triple=i686-apple-darwin8 -o %t %s -O2 &&
// RUNX: clang -emit-llvm -o %t %s // RUN: grep 'ret i32' %t | count 1 &&
// RUN: grep 'ret i32 1' %t | count 1
#include <stdio.h> #include <stdio.h>
@ -28,5 +29,17 @@ void foo(id a) {
} }
} }
int f0(id a) {
int x = 0;
@synchronized((x++, a)) {
}
return x; // ret i32 1
}
void f1(id a) {
// The trick here is that the return shouldn't go through clean up,
// but there isn't a simple way to check this property.
@synchronized(({ return; }), a) {
return;
}
}