From 54308191b6d18009e228caeffedbe577de2fae97 Mon Sep 17 00:00:00 2001
From: Will Dietz <w@wdtz.org>
Date: Fri, 24 Aug 2012 20:35:54 -0500
Subject: [PATCH 1/2] Add -fioc-{signed,shifts,strict-shifts} to check for undefined int behavior.

Requires corresponding compiler-rt change, IOC checks emit calls to
runtime functions to print diagnostic information about the checked behavior.
---
 include/clang/Basic/LangOptions.def   |    3 +
 include/clang/Driver/Options.td       |    6 +
 lib/CodeGen/CGExprScalar.cpp          |  226 +++++++++++++++++++++++++++-
 lib/CodeGen/CodeGenFunction.h         |   24 +++
 lib/Driver/Tools.cpp                  |   26 ++++
 lib/Frontend/CompilerInvocation.cpp   |    9 +
 runtime/compiler-rt/Makefile          |    4 +-
 test/CodeGen/ioc/div-and-rem.c        |   45 ++++++
 test/CodeGen/ioc/shifts.c             |   70 +++++++++
 test/CodeGen/ioc/signed.c             |  263 +++++++++++++++++++++++++++++++++
 test/CodeGen/ioc/unsigned-promotion.c |   93 ++++++++++++
 11 files changed, 759 insertions(+), 10 deletions(-)
 create mode 100644 test/CodeGen/ioc/div-and-rem.c
 create mode 100644 test/CodeGen/ioc/shifts.c
 create mode 100644 test/CodeGen/ioc/signed.c
 create mode 100644 test/CodeGen/ioc/unsigned-promotion.c

diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def
index acc1cf4..b341527 100644
--- a/include/clang/Basic/LangOptions.def
+++ b/include/clang/Basic/LangOptions.def
@@ -116,6 +116,9 @@ LANGOPT(CUDA              , 1, 0, "CUDA")
 LANGOPT(AssumeSaneOperatorNew , 1, 1, "implicit __attribute__((malloc)) for C++'s new operators")
 BENIGN_LANGOPT(ElideConstructors , 1, 1, "C++ copy constructor elision")
 BENIGN_LANGOPT(CatchUndefined    , 1, 0, "catching undefined behavior at run time")
+BENIGN_LANGOPT(IOCSignedOverflowChecks , 1, 0, "runtime checks for unsigned integer overflow")
+BENIGN_LANGOPT(IOCShiftChecks , 1, 0, "runtime checks for invalid shifts")
+BENIGN_LANGOPT(IOCStrictShiftChecks , 1, 0, "runtime checks for invalid shifts (C99/C++11 rules)")
 BENIGN_LANGOPT(DumpRecordLayouts , 1, 0, "dumping the layout of IRgen'd records")
 BENIGN_LANGOPT(DumpRecordLayoutsSimple , 1, 0, "dumping the layout of IRgen'd records in a simple form")
 BENIGN_LANGOPT(DumpVTableLayouts , 1, 0, "dumping the layouts of emitted vtables")
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index c7698a2..450480a 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -330,6 +330,12 @@ def fbuiltin : Flag<"-fbuiltin">, Group<f_Group>;
 def fcaret_diagnostics : Flag<"-fcaret-diagnostics">, Group<f_Group>;
 def fcatch_undefined_behavior : Flag<"-fcatch-undefined-behavior">, Flags<[CC1Option]>,
   Group<f_Group>, HelpText<"Generate runtime checks for undefined behavior.">;
+def fioc_signed : Flag <"-fioc-signed">, Flags<[CC1Option]>,
+  Group<f_Group>, HelpText<"Generate runtime checks for signed integer overflows.">;
+def fioc_shifts : Flag <"-fioc-shifts">, Flags<[CC1Option]>,
+  Group<f_Group>, HelpText<"Generate runtime checks for shifting more than bitwidth">;
+def fioc_strict_shifts : Flag <"-fioc-strict-shifts">, Flags<[CC1Option]>,
+  Group<f_Group>, HelpText<"Generate runtime checks for stricter C99/C++11 shift rules">;
 def fclasspath_EQ : Joined<"-fclasspath=">, Group<f_Group>;
 def fcolor_diagnostics : Flag<"-fcolor-diagnostics">, Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Use colors in diagnostics">;
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index dd5af52..ff7fd39 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -397,6 +397,8 @@ public:
       case LangOptions::SOB_Defined:
         return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul");
       case LangOptions::SOB_Undefined:
+        if (CGF.getContext().getLangOpts().IOCSignedOverflowChecks)
+          return EmitIOCBinOp(Ops);
         if (!CGF.CatchUndefined)
           return Builder.CreateNSWMul(Ops.LHS, Ops.RHS, "mul");
         // Fall through.
@@ -411,7 +413,8 @@ public:
   }
   bool isTrapvOverflowBehavior() {
     return CGF.getContext().getLangOpts().getSignedOverflowBehavior()
-               == LangOptions::SOB_Trapping || CGF.CatchUndefined;
+               == LangOptions::SOB_Trapping || CGF.CatchUndefined ||
+             CGF.getContext().getLangOpts().IOCSignedOverflowChecks;
   }
   /// Create a binary op that checks for overflow.
   /// Currently only supports +, - and *.
@@ -423,6 +426,9 @@ public:
     Builder.CreateCall(Trap);
     Builder.CreateUnreachable();
   }
+  /// Create a binary operation that checks for overflow
+  /// and emits call to the IOC runtime on check failure.
+  Value *EmitIOCBinOp(const BinOpInfo &Ops);
   // Check for undefined division and modulus behaviors.
   void EmitUndefinedBehaviorIntegerDivAndRemCheck(const BinOpInfo &Ops, 
                                                   llvm::Value *Zero,bool isDiv);
@@ -1256,6 +1262,15 @@ EmitAddConsiderOverflowBehavior(const UnaryOperator *E,
   case LangOptions::SOB_Defined:
     return Builder.CreateAdd(InVal, NextVal, IsInc ? "inc" : "dec");
   case LangOptions::SOB_Undefined:
+    if (CGF.getContext().getLangOpts().IOCSignedOverflowChecks) {
+      BinOpInfo BinOp;
+      BinOp.LHS = InVal;
+      BinOp.RHS = NextVal;
+      BinOp.Ty = E->getType();
+      BinOp.Opcode = BO_Add;
+      BinOp.E = E;
+      return EmitIOCBinOp(BinOp);
+    }
     if (!CGF.CatchUndefined)
       return Builder.CreateNSWAdd(InVal, NextVal, IsInc ? "inc" : "dec");
     // Fall through.
@@ -1783,12 +1798,20 @@ void ScalarExprEmitter::EmitUndefinedBehaviorIntegerDivAndRemCheck(
     CGF.Builder.CreateCondBr(Builder.CreateICmpEQ(Ops.RHS, Zero), 
                              overflowBB, contBB);
   }
-  EmitOverflowBB(overflowBB);
+
+  if (CGF.CGM.getLangOpts().IOCSignedOverflowChecks) {
+    bool isSigned = Ops.Ty->hasSignedIntegerRepresentation();
+    CGF.EmitIOCRTCallBB(overflowBB, contBB,
+                        isDiv ? CodeGenFunction::IOC_DIV_ERROR :
+                                CodeGenFunction::IOC_REM_ERROR,
+                        Ops.E, Ops.LHS, Ops.RHS, isSigned);
+  } else
+    EmitOverflowBB(overflowBB);
   Builder.SetInsertPoint(contBB);
 }
 
 Value *ScalarExprEmitter::EmitDiv(const BinOpInfo &Ops) {
-  if (isTrapvOverflowBehavior()) { 
+  if (isTrapvOverflowBehavior()) {
     llvm::Value *Zero = llvm::Constant::getNullValue(ConvertType(Ops.Ty));
 
     if (Ops.Ty->isIntegerType())
@@ -1924,6 +1947,161 @@ Value *ScalarExprEmitter::EmitOverflowCheckedBinOp(const BinOpInfo &Ops) {
   return phi;
 }
 
+Value *ScalarExprEmitter::EmitIOCBinOp(const BinOpInfo &Ops) {
+
+  // Find appropriate intrinsic and ioc check type for this operation
+  unsigned IID;
+  CodeGenFunction::IOCCheckType IOCCT;
+  switch (Ops.Opcode) {
+  case BO_Add:
+  case BO_AddAssign:
+    IID = llvm::Intrinsic::sadd_with_overflow;
+    IOCCT = CodeGenFunction::IOC_ADD;
+    break;
+  case BO_Sub:
+  case BO_SubAssign:
+    IID = llvm::Intrinsic::ssub_with_overflow;
+    IOCCT = CodeGenFunction::IOC_SUB;
+    break;
+  case BO_Mul:
+  case BO_MulAssign:
+    IID = llvm::Intrinsic::smul_with_overflow;
+    IOCCT = CodeGenFunction::IOC_MUL;
+    break;
+  default:
+    llvm_unreachable("Unsupported operation for overflow detection");
+  }
+
+  llvm::Type *opTy = CGF.CGM.getTypes().ConvertType(Ops.Ty);
+
+  llvm::Function *intrinsic = CGF.CGM.getIntrinsic(IID, opTy);
+
+  Value *resultAndOverflow = Builder.CreateCall2(intrinsic, Ops.LHS, Ops.RHS);
+  Value *result = Builder.CreateExtractValue(resultAndOverflow, 0);
+  Value *overflow = Builder.CreateExtractValue(resultAndOverflow, 1);
+
+  // Branch to overflowBB in case of overflow
+  llvm::BasicBlock *initialBB = Builder.GetInsertBlock();
+  llvm::Function::iterator insertPt = initialBB;
+  llvm::BasicBlock *continueBB = CGF.createBasicBlock("nooverflow", CGF.CurFn,
+                                                      llvm::next(insertPt));
+  llvm::BasicBlock *overflowBB = CGF.createBasicBlock("overflow", CGF.CurFn);
+
+  Builder.CreateCondBr(overflow, overflowBB, continueBB);
+
+  // Populate overflowBB with appropriate call to the runtime
+  CGF.EmitIOCRTCallBB(overflowBB, continueBB, IOCCT,
+                      Ops.E, Ops.LHS, Ops.RHS,
+                      Ops.E->getType()->isSignedIntegerOrEnumerationType());
+
+  Builder.SetInsertPoint(continueBB);
+  return result;
+}
+
+void CodeGenFunction::EmitIOCRTCallBB(llvm::BasicBlock *BB,
+                                      llvm::BasicBlock *ContBB,
+                                      CodeGenFunction::IOCCheckType IOCCT,
+                                      const Expr *E,
+                                      Value *LHS,
+                                      Value *RHS,
+                                      bool Signed) {
+  CGBuilderTy::InsertPoint IP = Builder.saveIP();
+  Builder.SetInsertPoint(BB);
+
+  // Extract filename and line/column information from the SourceLocation
+  unsigned Line, Column;
+  StringRef FileName;
+  {
+    SourceLocation SL = E->getExprLoc();
+    assert(SL.isValid());
+
+    SourceManager &SM = getContext().getSourceManager();
+    PresumedLoc PLoc = SM.getPresumedLoc(SL);
+    assert(PLoc.isValid());
+    Line = PLoc.getLine();
+    Column = PLoc.getColumn();
+    FileName = PLoc.getFilename();
+  }
+
+  // Create string for this expression.
+  // For now just get the opcode string, would be nice to
+  // generate a much prettier string containing the snippet
+  // or even more sophisticated caret-style using diagnostics.
+  SmallString<15> OpcodeStr = StringRef("<unknown>");
+  {
+    if (const BinaryOperator* BO = dyn_cast<BinaryOperator>(E))
+      OpcodeStr = BO->getOpcodeStr();
+    else if (const UnaryOperator * UO = dyn_cast<UnaryOperator>(E)) {
+      OpcodeStr = StringRef("unary ");
+      OpcodeStr += UnaryOperator::getOpcodeStr(UO->getOpcode());
+    }
+  }
+
+  // Build up the arguments
+  SmallVector<llvm::Value*, 8> Args;
+  Args.push_back(llvm::ConstantInt::get(Int32Ty, Line));
+  Args.push_back(llvm::ConstantInt::get(Int32Ty, Column));
+  Args.push_back(Builder.CreateGlobalStringPtr(FileName));
+  Args.push_back(Builder.CreateGlobalStringPtr(OpcodeStr));
+  Args.push_back(Signed ? Builder.CreateSExt(LHS, Int64Ty) :
+                          Builder.CreateZExt(LHS, Int64Ty));
+  Args.push_back(Signed ? Builder.CreateSExt(RHS, Int64Ty) :
+                          Builder.CreateZExt(RHS, Int64Ty));
+  Args.push_back(getIOCEncodedType(LHS->getType(), Signed));
+  assert(LHS->getType() == RHS->getType());
+
+  // Find the runtime function
+  llvm::Value *RTFunc;
+  {
+    StringRef RTFuncName;
+    switch (IOCCT) {
+    case IOC_ADD:          RTFuncName = "__ioc_report_add_overflow"; break;
+    case IOC_SUB:          RTFuncName = "__ioc_report_sub_overflow"; break;
+    case IOC_MUL:          RTFuncName = "__ioc_report_mul_overflow"; break;
+    case IOC_DIV_ERROR:    RTFuncName = "__ioc_report_div_error"; break;
+    case IOC_REM_ERROR:    RTFuncName = "__ioc_report_rem_error"; break;
+    case IOC_SHL_BITWIDTH: RTFuncName = "__ioc_report_shl_bitwidth"; break;
+    case IOC_SHL_STRICT:   RTFuncName = "__ioc_report_shl_strict"; break;
+    case IOC_SHR_BITWIDTH: RTFuncName = "__ioc_report_shr_bitwidth"; break;
+    }
+    llvm::Type* ArgTypes[] = {
+      Int32Ty, Int32Ty, /* Line, Column */
+      Int8PtrTy,        /* Filename */
+      Int8PtrTy,        /* Expression String */
+      Int64Ty, Int64Ty, /* LHS, RHS */
+      Int8Ty            /* Operand Type */
+    };
+    llvm::FunctionType *RTFuncTy =
+      llvm::FunctionType::get(VoidTy, ArgTypes, false);
+
+    RTFunc = CGM.CreateRuntimeFunction(RTFuncTy, RTFuncName);
+  }
+  assert(RTFunc);
+
+  // And finally emit a call the appropriate runtime function
+  Builder.CreateCall(RTFunc, Args);
+
+  // TODO: Make continue/unreachable a user-configurable option?
+  Builder.CreateBr(ContBB);
+
+  Builder.restoreIP(IP);
+}
+
+llvm::Value *CodeGenFunction::getIOCEncodedType(llvm::Type *T, bool isSigned) {
+  llvm::IntegerType *IntTy = cast<llvm::IntegerType>(T);
+
+  uint8_t Encoded = 0;
+  unsigned BitWidth = IntTy->getBitWidth();
+  assert(BitWidth <= 64 && "Unexpected large integer type in IOC encoding");
+  // Encode as log_2(BitWidth)
+  while (BitWidth >>= 1) ++Encoded;
+
+  // Set top bit iff the type is signed
+  Encoded |= isSigned ? 8 : 0;
+
+  return llvm::ConstantInt::get(Int8Ty, Encoded);
+}
+
 /// Emit pointer + index arithmetic.
 static Value *emitPointerArithmetic(CodeGenFunction &CGF,
                                     const BinOpInfo &op,
@@ -2017,6 +2195,8 @@ Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) {
     case LangOptions::SOB_Defined:
       return Builder.CreateAdd(op.LHS, op.RHS, "add");
     case LangOptions::SOB_Undefined:
+      if (CGF.getContext().getLangOpts().IOCSignedOverflowChecks)
+        return EmitIOCBinOp(op);
       if (!CGF.CatchUndefined)
         return Builder.CreateNSWAdd(op.LHS, op.RHS, "add");
       // Fall through.
@@ -2039,6 +2219,8 @@ Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) {
       case LangOptions::SOB_Defined:
         return Builder.CreateSub(op.LHS, op.RHS, "sub");
       case LangOptions::SOB_Undefined:
+        if (CGF.getContext().getLangOpts().IOCSignedOverflowChecks)
+          return EmitIOCBinOp(op);
         if (!CGF.CatchUndefined)
           return Builder.CreateNSWSub(op.LHS, op.RHS, "sub");
         // Fall through.
@@ -2118,17 +2300,29 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
   if (Ops.LHS->getType() != RHS->getType())
     RHS = Builder.CreateIntCast(RHS, Ops.LHS->getType(), false, "sh_prom");
 
-  if (CGF.CatchUndefined && isa<llvm::IntegerType>(Ops.LHS->getType())) {
+  bool IOCShift = CGF.getContext().getLangOpts().IOCShiftChecks;
+  bool IOCStrictShift = CGF.getContext().getLangOpts().IOCStrictShiftChecks;
+  if ((CGF.CatchUndefined || IOCShift || IOCStrictShift) &&
+      isa<llvm::IntegerType>(Ops.LHS->getType())) {
     unsigned Width = cast<llvm::IntegerType>(Ops.LHS->getType())->getBitWidth();
     llvm::BasicBlock *Cont = CGF.createBasicBlock("shl.cont");
-    llvm::BasicBlock *Trap = CGF.getTrapBB();
+    llvm::BasicBlock *Trap;
+    bool isSigned = Ops.Ty->isSignedIntegerType();
+    if (IOCShift || IOCStrictShift) {
+      Trap = CGF.createBasicBlock("shl.bitwidth", CGF.CurFn);
+      CGF.EmitIOCRTCallBB(Trap, Cont, CodeGenFunction::IOC_SHL_BITWIDTH,
+                          Ops.E, Ops.LHS, RHS, isSigned);
+    } else
+      Trap = CGF.getTrapBB();
+
     llvm::Value *WidthMinusOne =
       llvm::ConstantInt::get(RHS->getType(), Width - 1, "shl.width");
     CGF.Builder.CreateCondBr(Builder.CreateICmpULE(RHS, WidthMinusOne),
                              Cont, Trap);
     CGF.EmitBlock(Cont);
 
-    if (Ops.Ty->hasSignedIntegerRepresentation()) {
+    if (Ops.Ty->hasSignedIntegerRepresentation() &&
+        (CGF.CatchUndefined || IOCStrictShift)) {
       // Check whether we are shifting any non-zero bits off the top of the
       // integer.
       Cont = CGF.createBasicBlock("shl.ok");
@@ -2146,6 +2340,11 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
         BitsShiftedOff = Builder.CreateLShr(BitsShiftedOff, One);
       }
       llvm::Value *Zero = llvm::ConstantInt::get(BitsShiftedOff->getType(), 0);
+      if (IOCStrictShift) {
+        Trap = CGF.createBasicBlock("shl.signbit", CGF.CurFn);
+        CGF.EmitIOCRTCallBB(Trap, Cont, CodeGenFunction::IOC_SHL_STRICT,
+                            Ops.E, Ops.LHS, RHS, isSigned);
+      }
       Builder.CreateCondBr(Builder.CreateICmpEQ(BitsShiftedOff, Zero),
                            Cont, Trap);
       CGF.EmitBlock(Cont);
@@ -2162,12 +2361,23 @@ Value *ScalarExprEmitter::EmitShr(const BinOpInfo &Ops) {
   if (Ops.LHS->getType() != RHS->getType())
     RHS = Builder.CreateIntCast(RHS, Ops.LHS->getType(), false, "sh_prom");
 
-  if (CGF.CatchUndefined && isa<llvm::IntegerType>(Ops.LHS->getType())) {
+  bool IOCShiftCheck = CGF.getContext().getLangOpts().IOCShiftChecks ||
+                       CGF.getContext().getLangOpts().IOCStrictShiftChecks;
+  if ((CGF.CatchUndefined || IOCShiftCheck)
+      && isa<llvm::IntegerType>(Ops.LHS->getType())) {
     unsigned Width = cast<llvm::IntegerType>(Ops.LHS->getType())->getBitWidth();
     llvm::BasicBlock *Cont = CGF.createBasicBlock("cont");
+    llvm::BasicBlock *TrapBB;
+    if (IOCShiftCheck) {
+      TrapBB = CGF.createBasicBlock("shr.bitwidth", CGF.CurFn);
+      bool isSigned = Ops.Ty->isSignedIntegerType();
+      CGF.EmitIOCRTCallBB(TrapBB, Cont, CodeGenFunction::IOC_SHR_BITWIDTH,
+                          Ops.E, Ops.LHS, RHS, isSigned);
+    } else
+      TrapBB = CGF.getTrapBB();
     CGF.Builder.CreateCondBr(Builder.CreateICmpULT(RHS,
                                  llvm::ConstantInt::get(RHS->getType(), Width)),
-                             Cont, CGF.getTrapBB());
+                             Cont, TrapBB);
     CGF.EmitBlock(Cont);
   }
 
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index c74ef30..a363e91 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -1861,6 +1861,30 @@ public:
                                        bool isInc, bool isPre);
   ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV,
                                          bool isInc, bool isPre);
+
+  /// \brief Types of IOC checks emitted
+  enum IOCCheckType {
+    IOC_ADD,        /* Overflow */
+    IOC_SUB,        /* Overflow */
+    IOC_MUL,        /* Overflow */
+    IOC_DIV_ERROR,  /* Div by 0, or INT_MIN / -1 */
+    IOC_REM_ERROR,  /* Rem by 0, or INT_MIN % -1 */
+    IOC_SHL_BITWIDTH,
+    IOC_SHL_STRICT,
+    IOC_SHR_BITWIDTH
+  };
+
+  /// EmitIOCRTCallBB - Populate 'BB' with a call to the IOC runtime,
+  /// and and a branch to 'ContBB' to resume execution.
+  /// Preserves Builder's Insertion Point.
+  void EmitIOCRTCallBB(llvm::BasicBlock *BB, llvm::BasicBlock *ContBB,
+                       IOCCheckType CheckTy, const Expr *E,
+                       llvm::Value *LHS, llvm::Value *RHS, bool Signed);
+
+  /// getIOCEncodedType - Return the Value* encoding the given type.
+  /// For use in serializing the type to the IOC runtime.
+  llvm::Value *getIOCEncodedType(llvm::Type *T, bool isSigned);
+
   //===--------------------------------------------------------------------===//
   //                            Declaration Emission
   //===--------------------------------------------------------------------===//
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index 1142913..b98fcde 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -1448,6 +1448,28 @@ static void addTsanRTLinux(const ToolChain &TC, const ArgList &Args,
   }
 }
 
+static void addIOCRTLinux(const ToolChain &TC, const ArgList &Args,
+                           ArgStringList &CmdArgs) {
+  // If no IOC flag was given, there's nothing to do.
+  if (!Args.hasArg(options::OPT_fioc_signed) &&
+      !Args.hasArg(options::OPT_fioc_shifts) &&
+      !Args.hasArg(options::OPT_fioc_strict_shifts))
+    return;
+
+  if (!Args.hasArg(options::OPT_shared)) {
+    // LibIOC is "libclang_rt.ioc-<ArchName>.a" in the Linux library
+    // resource directory.
+    SmallString<128> LibIOC(TC.getDriver().ResourceDir);
+    llvm::sys::path::append(LibIOC, "lib", "linux",
+                            (Twine("libclang_rt.ioc-") +
+                             TC.getArchName() + ".a"));
+    CmdArgs.push_back(Args.MakeArgString(LibIOC));
+    CmdArgs.push_back("-lpthread");
+    CmdArgs.push_back("-ldl");
+    CmdArgs.push_back("-export-dynamic");
+  }
+}
+
 static bool shouldUseFramePointer(const ArgList &Args,
                                   const llvm::Triple &Triple) {
   if (Arg *A = Args.getLastArg(options::OPT_fno_omit_frame_pointer,
@@ -2313,6 +2335,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_parseable_fixits);
   Args.AddLastArg(CmdArgs, options::OPT_ftime_report);
   Args.AddLastArg(CmdArgs, options::OPT_ftrapv);
+  Args.AddLastArg(CmdArgs, options::OPT_fioc_signed);
+  Args.AddLastArg(CmdArgs, options::OPT_fioc_shifts);
+  Args.AddLastArg(CmdArgs, options::OPT_fioc_strict_shifts);
 
   if (Arg *A = Args.getLastArg(options::OPT_ftrapv_handler_EQ)) {
     CmdArgs.push_back("-ftrapv-handler");
@@ -5755,6 +5780,7 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA,
   // Call this before we add the C run-time.
   addAsanRTLinux(getToolChain(), Args, CmdArgs);
   addTsanRTLinux(getToolChain(), Args, CmdArgs);
+  addIOCRTLinux(getToolChain(), Args, CmdArgs);
 
   if (!Args.hasArg(options::OPT_nostdlib)) {
     if (!Args.hasArg(options::OPT_nodefaultlibs)) {
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 4f166b0..e5c9585 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -709,6 +709,12 @@ static void LangOptsToArgs(const LangOptions &Opts, ToArgsList &Res) {
     Res.push_back("-fpascal-strings");
   if (Opts.CatchUndefined)
     Res.push_back("-fcatch-undefined-behavior");
+  if (Opts.IOCSignedOverflowChecks)
+    Res.push_back("-fioc-signed");
+  if (Opts.IOCShiftChecks)
+    Res.push_back("-fioc-shifts");
+  if (Opts.IOCStrictShiftChecks)
+    Res.push_back("-fioc-strict-shifts");
   if (Opts.AddressSanitizer)
     Res.push_back("-faddress-sanitizer");
   if (Opts.ThreadSanitizer)
@@ -2068,6 +2074,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
   Opts.ObjCDefaultSynthProperties =
     Args.hasArg(OPT_fobjc_default_synthesize_properties);
   Opts.CatchUndefined = Args.hasArg(OPT_fcatch_undefined_behavior);
+  Opts.IOCSignedOverflowChecks = Args.hasArg(OPT_fioc_signed);
+  Opts.IOCShiftChecks = Args.hasArg(OPT_fioc_shifts);
+  Opts.IOCStrictShiftChecks = Args.hasArg(OPT_fioc_strict_shifts);
   Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls);
   Opts.PackStruct = Args.getLastArgIntValue(OPT_fpack_struct_EQ, 0, Diags);
   Opts.PICLevel = Args.getLastArgIntValue(OPT_pic_level, 0, Diags);
diff --git a/runtime/compiler-rt/Makefile b/runtime/compiler-rt/Makefile
index f133591..f451e2f 100644
--- a/runtime/compiler-rt/Makefile
+++ b/runtime/compiler-rt/Makefile
@@ -86,11 +86,11 @@ RuntimeLibrary.linux.Configs :=
 # We currently only try to generate runtime libraries on x86.
 ifeq ($(ARCH),x86)
 RuntimeLibrary.linux.Configs += \
-	full-i386 profile-i386 asan-i386
+	full-i386 profile-i386 asan-i386 ioc-i386
 endif
 ifeq ($(ARCH),x86_64)
 RuntimeLibrary.linux.Configs += \
-	full-x86_64 profile-x86_64 asan-x86_64 tsan-x86_64
+	full-x86_64 profile-x86_64 asan-x86_64 tsan-x86_64 ioc-x86_64
 endif
 
 endif
diff --git a/test/CodeGen/ioc/div-and-rem.c b/test/CodeGen/ioc/div-and-rem.c
new file mode 100644
index 0000000..3b3af17
--- /dev/null
+++ b/test/CodeGen/ioc/div-and-rem.c
@@ -0,0 +1,45 @@
+// Test div and rem checks added with -fioc-signed
+//
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - \
+// RUN: -fioc-signed| FileCheck %s
+
+unsigned u, v, w;
+int i, j, k;
+
+// CHECK: define void @testdiv
+void testdiv() {
+  // CHECK:      [[T1:%.*]] = load i32* @j
+  // CHECK-NEXT: [[T2:%.*]] = load i32* @k
+  // CHECK:      icmp eq i32 [[T2]], 0
+  // CHECK:      icmp eq i32 [[T1]], -2147483648
+  // CHECK:      icmp eq i32 [[T2]], -1
+  // CHECK:      sdiv
+  i = j / k;
+
+  // CHECK:      [[T1:%.*]] = load i32* @v
+  // CHECK-NEXT: [[T2:%.*]] = load i32* @w
+  // CHECK:      icmp eq i32 [[T2]], 0
+  // CHECK-NOT:  icmp eq {{.*}}, -2147483648
+  // CHECK-NOT:  icmp eq {{.*}}, -1
+  // CHECK:      udiv
+  u = v / w;
+}
+
+// CHECK: define void @testrem
+void testrem() {
+  // CHECK:      [[T1:%.*]] = load i32* @j
+  // CHECK-NEXT: [[T2:%.*]] = load i32* @k
+  // CHECK:      icmp eq i32 [[T2]], 0
+  // CHECK:      icmp eq i32 [[T1]], -2147483648
+  // CHECK:      icmp eq i32 [[T2]], -1
+  // CHECK:      srem
+  i = j % k;
+
+  // CHECK:      [[T1:%.*]] = load i32* @v
+  // CHECK-NEXT: [[T2:%.*]] = load i32* @w
+  // CHECK:      icmp eq i32 [[T2]], 0
+  // CHECK-NOT:  icmp eq {{.*}}, -2147483648
+  // CHECK-NOT:  icmp eq {{.*}}, -1
+  // CHECK:      urem
+  u = v % w;
+}
diff --git a/test/CodeGen/ioc/shifts.c b/test/CodeGen/ioc/shifts.c
new file mode 100644
index 0000000..697d6b4
--- /dev/null
+++ b/test/CodeGen/ioc/shifts.c
@@ -0,0 +1,70 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - \
+// RUN:   -fioc-shifts | FileCheck %s --check-prefix=SHIFT
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 %s -emit-llvm -o - \
+// RUN:   -fioc-strict-shifts | FileCheck %s --check-prefix=STRICT
+
+unsigned int u1, u2, u3;
+  signed int s1, s2, s3;
+
+// SHIFT: define void @testSShl()
+// STRICT: define void @testSShl()
+void testSShl() {
+  // SHIFT: [[T1:%.*]] = load i32* @s2
+  // SHIFT: [[T2:%.*]] = load i32* @s3
+  // SHIFT: icmp ule i32 [[T2]], 31
+  // SHIFT: call void @__ioc_report_shl_bitwidth
+  // STRICT: [[T1:%.*]] = load i32* @s2
+  // STRICT: [[T2:%.*]] = load i32* @s3
+  // STRICT: icmp ule i32 [[T2]], 31
+  // STRICT: call void @__ioc_report_shl_bitwidth
+  // STRICT: call void @__ioc_report_shl_strict
+  s1 = s2 << s3;
+}
+
+// SHIFT: define void @testSShr()
+// STRICT: define void @testSShr()
+void testSShr() {
+  // SHIFT:     [[T1:%.*]] = load i32* @s2
+  // SHIFT:     [[T2:%.*]] = load i32* @s3
+  // SHIFT:     icmp ult i32 [[T2]], 32
+  // SHIFT:     call void @__ioc_report_shr_bitwidth
+  // SHIFT-NOT: call void @__ioc_report_shr_strict
+  // STRICT:     [[T1:%.*]] = load i32* @s2
+  // STRICT:     [[T2:%.*]] = load i32* @s3
+  // STRICT:     icmp ult i32 [[T2]], 32
+  // STRICT:     call void @__ioc_report_shr_bitwidth
+  // STRICT-NOT: call void @__ioc_report_shr_strict
+  s1 = s2 >> s3;
+}
+
+// SHIFT: define void @testUShl()
+// STRICT: define void @testUShl()
+void testUShl() {
+  // SHIFT:     [[T1:%.*]] = load i32* @u2
+  // SHIFT:     [[T2:%.*]] = load i32* @u3
+  // SHIFT:     icmp ule i32 [[T2]], 31
+  // SHIFT:     call void @__ioc_report_shl_bitwidth
+  // SHIFT-NOT: call void @__ioc_report_shl_strict
+  // STRICT:     [[T1:%.*]] = load i32* @u2
+  // STRICT:     [[T2:%.*]] = load i32* @u3
+  // STRICT:     icmp ule i32 [[T2]], 31
+  // STRICT:     call void @__ioc_report_shl_bitwidth
+  // STRICT-NOT: call void @__ioc_report_shl_strict
+  u1 = u2 << u3;
+}
+
+// SHIFT: define void @testUShr()
+// STRICT: define void @testUShr()
+void testUShr() {
+  // SHIFT:     [[T1:%.*]] = load i32* @u2
+  // SHIFT:     [[T2:%.*]] = load i32* @u3
+  // SHIFT:     icmp ult i32 [[T2]], 32
+  // SHIFT:     call void @__ioc_report_shr_bitwidth
+  // SHIFT-NOT: call void @__ioc_report_shr_strict
+  // STRICT:     [[T1:%.*]] = load i32* @u2
+  // STRICT:     [[T2:%.*]] = load i32* @u3
+  // STRICT:     icmp ult i32 [[T2]], 32
+  // STRICT:     call void @__ioc_report_shr_bitwidth
+  // STRICT-NOT: call void @__ioc_report_shr_strict
+  u1 = u2 >> u3;
+}
diff --git a/test/CodeGen/ioc/signed.c b/test/CodeGen/ioc/signed.c
new file mode 100644
index 0000000..6977174
--- /dev/null
+++ b/test/CodeGen/ioc/signed.c
@@ -0,0 +1,263 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fioc-signed %s -emit-llvm -o - | FileCheck %s
+// Verify -fioc-signed behavior for +,-,*, and ++ for various datatypes.
+//
+// Post/preincrement on types smaller than int don't get instrumented,
+// as they cannot overflow due to promotion.
+
+long li, lj, lk;
+int ii, ij, ik;
+short si, sj, sk;
+char ci, cj, ck;
+
+extern void opaquelong(long);
+extern void opaqueint(int);
+extern void opaqueshort(short);
+extern void opaquechar(char);
+
+// CHECK: define void @testlongadd()
+void testlongadd() {
+
+  // CHECK:      [[T1:%.*]] = load i64* @lj
+  // CHECK-NEXT: [[T2:%.*]] = load i64* @lk
+  // CHECK-NEXT: [[T3:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[T1]], i64 [[T2]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T3]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i64, i1 } [[T3]], 1
+  // CHECK-NEXT: br i1 [[T5]], label %[[T6:.+]], label %[[T7:.+]]
+  // CHECK:      {{^}}[[T6]]:
+  // CHECK:      call void @__ioc_report_add_overflow
+  li = lj + lk;
+}
+
+// CHECK: define void @testlongsub()
+void testlongsub() {
+
+  // CHECK:      [[T1:%.*]] = load i64* @lj
+  // CHECK-NEXT: [[T2:%.*]] = load i64* @lk
+  // CHECK-NEXT: [[T3:%.*]] = call { i64, i1 } @llvm.ssub.with.overflow.i64(i64 [[T1]], i64 [[T2]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T3]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i64, i1 } [[T3]], 1
+  // CHECK-NEXT: br i1 [[T5]], label %[[T6:.+]], label %[[T7:.+]]
+  // CHECK:      {{^}}[[T6]]:
+  // CHECK:      call void @__ioc_report_sub_overflow
+  li = lj - lk;
+}
+
+// CHECK: define void @testlongmul()
+void testlongmul() {
+
+  // CHECK:      [[T1:%.*]] = load i64* @lj
+  // CHECK-NEXT: [[T2:%.*]] = load i64* @lk
+  // CHECK-NEXT: [[T3:%.*]] = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 [[T1]], i64 [[T2]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T3]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i64, i1 } [[T3]], 1
+  // CHECK-NEXT: br i1 [[T5]], label %[[T6:.+]], label %[[T7:.+]]
+  // CHECK:      {{^}}[[T6]]:
+  // CHECK:      call void @__ioc_report_mul_overflow
+  li = lj * lk;
+}
+
+// CHECK: define void @testlongpostinc()
+void testlongpostinc() {
+  opaquelong(li++);
+
+  // CHECK:      [[T1:%.*]] = load i64* @li
+  // CHECK-NEXT: [[T2:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[T1]], i64 1)
+  // CHECK-NEXT: [[T3:%.*]] = extractvalue { i64, i1 } [[T2]], 0
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T2]], 1
+  // CHECK-NEXT: br i1 [[T4]]
+  // CHECK:      call void @__ioc_report_add_overflow
+}
+
+// CHECK: define void @testlongpreinc()
+void testlongpreinc() {
+  opaquelong(++li);
+
+  // CHECK:      [[T1:%.*]] = load i64* @li
+  // CHECK-NEXT: [[T2:%.*]] = call { i64, i1 } @llvm.sadd.with.overflow.i64(i64 [[T1]], i64 1)
+  // CHECK-NEXT: [[T3:%.*]] = extractvalue { i64, i1 } [[T2]], 0
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i64, i1 } [[T2]], 1
+  // CHECK-NEXT: br i1 [[T4]]
+  // CHECK:      call void @__ioc_report_add_overflow
+}
+
+// CHECK: define void @testintadd()
+void testintadd() {
+
+  // CHECK:      [[T1:%.*]] = load i32* @ij
+  // CHECK-NEXT: [[T2:%.*]] = load i32* @ik
+  // CHECK-NEXT: [[T3:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T1]], i32 [[T2]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T3]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T3]], 1
+  // CHECK-NEXT: br i1 [[T5]], label %[[T6:.+]], label %[[T7:.+]]
+  // CHECK:      {{^}}[[T6]]:
+  // CHECK:      call void @__ioc_report_add_overflow
+  ii = ij + ik;
+}
+
+// CHECK: define void @testintsub()
+void testintsub() {
+
+  // CHECK:      [[T1:%.*]] = load i32* @ij
+  // CHECK-NEXT: [[T2:%.*]] = load i32* @ik
+  // CHECK-NEXT: [[T3:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[T1]], i32 [[T2]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T3]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T3]], 1
+  // CHECK-NEXT: br i1 [[T5]], label %[[T6:.+]], label %[[T7:.+]]
+  // CHECK:      {{^}}[[T6]]:
+  // CHECK:      call void @__ioc_report_sub_overflow
+  ii = ij - ik;
+}
+
+// CHECK: define void @testintmul()
+void testintmul() {
+
+  // CHECK:      [[T1:%.*]] = load i32* @ij
+  // CHECK-NEXT: [[T2:%.*]] = load i32* @ik
+  // CHECK-NEXT: [[T3:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[T1]], i32 [[T2]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T3]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T3]], 1
+  // CHECK-NEXT: br i1 [[T5]], label %[[T6:.+]], label %[[T7:.+]]
+  // CHECK:      {{^}}[[T6]]:
+  // CHECK:      call void @__ioc_report_mul_overflow
+  ii = ij * ik;
+}
+
+// CHECK: define void @testintpostinc()
+void testintpostinc() {
+  opaqueint(ii++);
+
+  // CHECK:      [[T1:%.*]] = load i32* @ii
+  // CHECK-NEXT: [[T2:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T1]], i32 1)
+  // CHECK-NEXT: [[T3:%.*]] = extractvalue { i32, i1 } [[T2]], 0
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T2]], 1
+  // CHECK-NEXT: br i1 [[T4]]
+  // CHECK:      call void @__ioc_report_add_overflow
+}
+
+// CHECK: define void @testintpreinc()
+void testintpreinc() {
+  opaqueint(++ii);
+
+  // CHECK:      [[T1:%.*]] = load i32* @ii
+  // CHECK-NEXT: [[T2:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T1]], i32 1)
+  // CHECK-NEXT: [[T3:%.*]] = extractvalue { i32, i1 } [[T2]], 0
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T2]], 1
+  // CHECK-NEXT: br i1 [[T4]]
+  // CHECK:      call void @__ioc_report_add_overflow
+}
+
+// CHECK: define void @testshortadd()
+void testshortadd() {
+
+  // CHECK:      load i16* @sj
+  // CHECK:      load i16* @sk
+  // CHECK:      [[T1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECK:      call void @__ioc_report_add_overflow
+  si = sj + sk;
+}
+
+// CHECK: define void @testshortsub()
+void testshortsub() {
+
+  // CHECK:      load i16* @sj
+  // CHECK:      load i16* @sk
+  // CHECK:      [[T1:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECK:      call void @__ioc_report_sub_overflow
+  si = sj - sk;
+}
+
+// CHECK: define void @testshortmul()
+void testshortmul() {
+
+  // CHECK:      load i16* @sj
+  // CHECK:      load i16* @sk
+  // CHECK:      [[T1:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECK:      call void @__ioc_report_mul_overflow
+  si = sj * sk;
+}
+
+// CHECK: define void @testshortpostinc()
+void testshortpostinc() {
+  opaqueshort(si++);
+
+  // CHECK:      [[T1:%.*]] = load i16* @si
+  // CHECK-NEXT: [[T2:%.*]] = add i16 [[T1]], 1
+  // CHECK-NOT:  llvm.sadd
+  // CHECK-NOT:  llvm.uadd
+  // CHECK:      ret void
+}
+
+// CHECK: define void @testshortpreinc()
+void testshortpreinc() {
+  opaqueshort(++si);
+
+  // CHECK:      [[T1:%.*]] = load i16* @si
+  // CHECK-NEXT: [[T2:%.*]] = add i16 [[T1]], 1
+  // CHECK-NOT:  llvm.sadd
+  // CHECK-NOT:  llvm.uadd
+  // CHECK:      ret void
+}
+
+// CHECK: define void @testcharadd()
+void testcharadd() {
+
+  // CHECK:      load i8* @cj
+  // CHECK:      load i8* @ck
+  // CHECK:      [[T1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECK:      call void @__ioc_report_add_overflow
+  ci = cj + ck;
+}
+
+// CHECK: define void @testcharsub()
+void testcharsub() {
+
+  // CHECK:      load i8* @cj
+  // CHECK:      load i8* @ck
+  // CHECK:      [[T1:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECK:      call void @__ioc_report_sub_overflow
+  ci = cj - ck;
+}
+
+// CHECK: define void @testcharmul()
+void testcharmul() {
+
+  // CHECK:      load i8* @cj
+  // CHECK:      load i8* @ck
+  // CHECK:      [[T1:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECK:      call void @__ioc_report_mul_overflow
+  ci = cj * ck;
+}
+
+// CHECK: define void @testcharpostinc()
+void testcharpostinc() {
+  opaquechar(ci++);
+
+  // CHECK:      [[T1:%.*]] = load i8* @ci
+  // CHECK-NEXT: [[T2:%.*]] = add i8 [[T1]], 1
+  // CHECK-NOT:  llvm.sadd
+  // CHECK-NOT:  llvm.uadd
+  // CHECK:      ret void
+}
+
+// CHECK: define void @testcharpreinc()
+void testcharpreinc() {
+  opaquechar(++ci);
+
+  // CHECK:      [[T1:%.*]] = load i8* @ci
+  // CHECK-NEXT: [[T2:%.*]] = add i8 [[T1]], 1
+  // CHECK-NOT:  llvm.sadd
+  // CHECK-NOT:  llvm.uadd
+  // CHECK:      ret void
+}
diff --git a/test/CodeGen/ioc/unsigned-promotion.c b/test/CodeGen/ioc/unsigned-promotion.c
new file mode 100644
index 0000000..a0c299b
--- /dev/null
+++ b/test/CodeGen/ioc/unsigned-promotion.c
@@ -0,0 +1,93 @@
+// Check -fioc-signed on promoted unsigned types
+//
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s \
+// RUN:   -fioc-signed | FileCheck %s --check-prefix=CHECKS
+
+unsigned short si, sj, sk;
+unsigned char ci, cj, ck;
+
+extern void opaqueshort(unsigned short);
+extern void opaquechar(unsigned char);
+
+// CHECKS:   define void @testshortadd()
+void testshortadd() {
+  // CHECKS:        load i16* @sj
+  // CHECKS:        load i16* @sk
+  // CHECKS:        [[T1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECKS-NEXT:   [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECKS-NEXT:   [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECKS:        call void @__ioc_report_add_overflow
+  //
+
+  si = sj + sk;
+}
+
+// CHECKS:   define void @testshortsub()
+void testshortsub() {
+
+  // CHECKS:        load i16* @sj
+  // CHECKS:        load i16* @sk
+  // CHECKS:        [[T1:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECKS-NEXT:   [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECKS-NEXT:   [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECKS:        call void @__ioc_report_sub_overflow
+  //
+
+  si = sj - sk;
+}
+
+// CHECKS:   define void @testshortmul()
+void testshortmul() {
+
+  // CHECKS:        load i16* @sj
+  // CHECKS:        load i16* @sk
+  // CHECKS:        [[T1:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECKS-NEXT:   [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECKS-NEXT:   [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECKS:        call void @__ioc_report_mul_overflow
+  //
+
+  si = sj * sk;
+}
+
+// CHECKS:   define void @testcharadd()
+void testcharadd() {
+
+  // CHECKS:        load i8* @cj
+  // CHECKS:        load i8* @ck
+  // CHECKS:        [[T1:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECKS-NEXT:   [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECKS-NEXT:   [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECKS:        call void @__ioc_report_add_overflow
+  //
+
+  ci = cj + ck;
+}
+
+// CHECKS:   define void @testcharsub()
+void testcharsub() {
+
+  // CHECKS:        load i8* @cj
+  // CHECKS:        load i8* @ck
+  // CHECKS:        [[T1:%.*]] = call { i32, i1 } @llvm.ssub.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECKS-NEXT:   [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECKS-NEXT:   [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECKS:        call void @__ioc_report_sub_overflow
+  //
+
+  ci = cj - ck;
+}
+
+// CHECKS:   define void @testcharmul()
+void testcharmul() {
+
+  // CHECKS:        load i8* @cj
+  // CHECKS:        load i8* @ck
+  // CHECKS:        [[T1:%.*]] = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 [[T2:%.*]], i32 [[T3:%.*]])
+  // CHECKS-NEXT:   [[T4:%.*]] = extractvalue { i32, i1 } [[T1]], 0
+  // CHECKS-NEXT:   [[T5:%.*]] = extractvalue { i32, i1 } [[T1]], 1
+  // CHECKS:        call void @__ioc_report_mul_overflow
+  //
+
+  ci = cj * ck;
+}
-- 
1.7.1

