diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h
index 40da070..41fc982 100644
--- a/include/clang/AST/Expr.h
+++ b/include/clang/AST/Expr.h
@@ -4332,6 +4332,16 @@ public:
   child_range children() { return child_range(&SrcExpr, &SrcExpr+1); }
 };
 
+/// DummyTypeExpr - Not a real expression, but a simple container for a type.
+/// Should be used to pass type arguments to attributes, like OpenCL's
+/// vec_type_hint.
+class DummyTypeExpr : public Expr {
+public:
+  DummyTypeExpr(QualType type)
+    : Expr(NoStmtClass, type, VK_RValue, OK_Ordinary,
+           false, false, false, false) {}
+};
+
 /// PseudoObjectExpr - An expression which accesses a pseudo-object
 /// l-value.  A pseudo-object is an abstract object, accesses to which
 /// are translated to calls.  The pseudo-object expression has a
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
index 2e23c64..105d569 100644
--- a/include/clang/Basic/Attr.td
+++ b/include/clang/Basic/Attr.td
@@ -569,6 +569,23 @@ def ReqdWorkGroupSize : InheritableAttr {
               UnsignedArgument<"ZDim">];
 }
 
+def Endian : InheritableAttr {
+  let Spellings = ["endian"];
+  let Args = [IdentifierArgument<"platform">];
+}
+
+def VecTypeHint : InheritableAttr {
+  let Spellings = ["vec_type_hint"];
+  let Args = [TypeArgument<"TypeHint">, SourceLocArgument<"TypeLoc">];
+}
+
+def WorkGroupSizeHint :  InheritableAttr {
+  let Spellings = ["work_group_size_hint"];
+  let Args = [UnsignedArgument<"XDim">, 
+              UnsignedArgument<"YDim">,
+              UnsignedArgument<"ZDim">];
+}
+
 def InitPriority : InheritableAttr {
   let Spellings = ["init_priority"];
   let Args = [UnsignedArgument<"Priority">];
@@ -648,13 +665,6 @@ def VectorSize : Attr {
   let ASTNode = 0;
 }
 
-def VecTypeHint : Attr {
-  let Spellings = ["vec_type_hint"];
-  let ASTNode = 0;
-  let SemaHandler = 0;
-  let Ignored = 1;
-}
-
 def Visibility : InheritableAttr {
   let Clone = 0;
   let Spellings = ["visibility"];
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 3dc03fa..4fa0b38 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1505,6 +1505,8 @@ def err_attribute_argument_outof_range : Error<
 def err_init_priority_object_attr : Error<
   "can only use 'init_priority' attribute on file-scope definitions "
   "of objects of class type">;
+def err_attribute_argument_invalid_type : Error<
+  "invalid attribute argument %0 - expecting a vector or vectorizable scalar type">;
 def err_attribute_argument_duplicate: Error<
   "'%0' attribute parameter %1 is duplicated">;
 def err_attribute_argument_n_not_int : Error<
@@ -5447,6 +5449,9 @@ def warn_collection_expr_type : Warning<
 def err_invalid_conversion_between_ext_vectors : Error<
   "invalid conversion between ext-vector type %0 and %1">;
 
+def warn_duplicate_attribute : Warning<
+  "attribute %0 is already applied with different parameters">;
+
 // Type
 def ext_invalid_sign_spec : Extension<"'%0' cannot be signed or unsigned">;
 def warn_receiver_forward_class : Warning<
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index f0d3213..588b7cf 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -3131,6 +3131,8 @@ public:
                              SourceLocation BuiltinLoc,
                              SourceLocation RParenLoc);
 
+  ExprResult ActOnDummyTypeExpr(ParsedType ParsedTy);
+
   //===---------------------------- C++ Features --------------------------===//
 
   // Act on C++ namespaces
diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp
index dfb04b4..a5b82dc 100644
--- a/lib/CodeGen/CodeGenFunction.cpp
+++ b/lib/CodeGen/CodeGenFunction.cpp
@@ -251,6 +251,70 @@ void CodeGenFunction::EmitMCountInstrumentation() {
   Builder.CreateCall(MCountFn);
 }
 
+void CodeGenFunction::EmitOpenCLKernelMetadata(const FunctionDecl *FD, 
+                                               llvm::Function *Fn)
+{
+  if (!FD->hasAttr<OpenCLKernelAttr>())
+    return;
+
+  llvm::LLVMContext &Context = getLLVMContext();
+
+  llvm::SmallVector <llvm::Value*, 5> kernelMDArgs;
+  kernelMDArgs.push_back(Fn);
+
+  if (FD->hasAttr<VecTypeHintAttr>()) {
+    llvm::SmallVector <llvm::Value*, 3> attrMDArgs;
+    attrMDArgs.push_back(llvm::MDString::get(Context, "vec_type_hint"));
+
+    VecTypeHintAttr *attr = FD->getAttr<VecTypeHintAttr>();
+    QualType hintQTy = attr->getTypeHint();
+    llvm::Type *hintTy = CGM.getTypes().ConvertType(hintQTy);
+    attrMDArgs.push_back(llvm::UndefValue::get(hintTy));
+
+    const ExtVectorType *hintEltQTy = hintQTy->getAs<ExtVectorType>();
+    bool isSignedInteger = hintQTy->isSignedIntegerType() ||
+       (hintEltQTy && hintEltQTy->getElementType()->isSignedIntegerType());
+    attrMDArgs.push_back(llvm::ConstantInt::get(
+       llvm::IntegerType::get(Context, 32),
+       llvm::APInt(32, (uint64_t) (isSignedInteger?1:0))));
+
+    kernelMDArgs.push_back(llvm::MDNode::get(Context, attrMDArgs));
+  }
+
+  if (FD->hasAttr<WorkGroupSizeHintAttr>()) {
+    llvm::SmallVector <llvm::Value*, 5> attrMDArgs;
+    attrMDArgs.push_back(llvm::MDString::get(Context, "work_group_size_hint"));
+    WorkGroupSizeHintAttr *attr = FD->getAttr<WorkGroupSizeHintAttr>();
+    llvm::Type *iTy = llvm::IntegerType::get(Context, 32);
+    attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+       llvm::APInt(32, (uint64_t)attr->getXDim())));
+    attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+       llvm::APInt(32, (uint64_t)attr->getYDim())));
+    attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+       llvm::APInt(32, (uint64_t)attr->getZDim())));
+    kernelMDArgs.push_back(llvm::MDNode::get(Context, attrMDArgs));
+  }
+
+  if (FD->hasAttr<ReqdWorkGroupSizeAttr>()) {
+    llvm::SmallVector <llvm::Value*, 5> attrMDArgs;
+    attrMDArgs.push_back(llvm::MDString::get(Context, "reqd_work_group_size"));
+    ReqdWorkGroupSizeAttr *attr = FD->getAttr<ReqdWorkGroupSizeAttr>();
+    llvm::Type *iTy = llvm::IntegerType::get(Context, 32);
+    attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+       llvm::APInt(32, (uint64_t)attr->getXDim())));
+    attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+       llvm::APInt(32, (uint64_t)attr->getYDim())));
+    attrMDArgs.push_back(llvm::ConstantInt::get(iTy,
+       llvm::APInt(32, (uint64_t)attr->getZDim())));
+    kernelMDArgs.push_back(llvm::MDNode::get(Context, attrMDArgs));
+  }
+
+  llvm::MDNode *kernelMDNode = llvm::MDNode::get(Context, kernelMDArgs);
+  llvm::NamedMDNode *OpenCLKernelMetadata =
+    CGM.getModule().getOrInsertNamedMetadata("opencl.kernels");
+  OpenCLKernelMetadata->addOperand(kernelMDNode);
+}
+
 void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
                                     llvm::Function *Fn,
                                     const CGFunctionInfo &FnInfo,
@@ -279,14 +343,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
   if (getContext().getLangOpts().OpenCL) {
     // Add metadata for a kernel function.
     if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D))
-      if (FD->hasAttr<OpenCLKernelAttr>()) {
-        llvm::LLVMContext &Context = getLLVMContext();
-        llvm::NamedMDNode *OpenCLMetadata = 
-          CGM.getModule().getOrInsertNamedMetadata("opencl.kernels");
-          
-        llvm::Value *Op = Fn;
-        OpenCLMetadata->addOperand(llvm::MDNode::get(Context, Op));
-      }
+      EmitOpenCLKernelMetadata(FD, Fn);
   }
 
   llvm::BasicBlock *EntryBB = createBasicBlock("entry", CurFn);
diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h
index de7e89e..72e1d63 100644
--- a/lib/CodeGen/CodeGenFunction.h
+++ b/lib/CodeGen/CodeGenFunction.h
@@ -1197,6 +1197,19 @@ private:
   llvm::BasicBlock *TerminateHandler;
   llvm::BasicBlock *TrapBB;
 
+  /// Add a kernel metadata node to the named metadata node 'opencl.kernels'.
+  /// In the kernel metadata node, reference the kernel function and metadata 
+  /// nodes for its optional attribute qualifiers (OpenCL 1.1 6.7.2):
+  /// - A node for the vec_type_hint(<type>) qualifier contains string 
+  ///   "vec_type_hint", an undefined value of the <type> data type,
+  ///   and a Boolean that is true if the <type> is integer and signed.
+  /// - A node for the work_group_size_hint(X,Y,Z) qualifier contains string 
+  ///   "work_group_size_hint", and three 32-bit integers X, Y and Z.
+  /// - A node for the reqd_work_group_size(X,Y,Z) qualifier contains string 
+  ///   "reqd_work_group_size", and three 32-bit integers X, Y and Z.
+  void EmitOpenCLKernelMetadata(const FunctionDecl *FD, 
+                                llvm::Function *Fn);
+
 public:
   CodeGenFunction(CodeGenModule &cgm);
   ~CodeGenFunction();
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index ac63780..f5a67cc 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -199,6 +199,10 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
   IdentifierInfo *ParmName = 0;
   SourceLocation ParmLoc;
   bool BuiltinType = false;
+  bool TypeParsed = false;
+
+  TypeResult T;
+  SourceRange TypeRange;
 
   switch (Tok.getKind()) {
   case tok::kw_char:
@@ -218,12 +222,16 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
   case tok::kw_void:
   case tok::kw_typeof:
     // __attribute__(( vec_type_hint(char) ))
-    // FIXME: Don't just discard the builtin type token.
-    ConsumeToken();
-    BuiltinType = true;
+    BuiltinType = true; TypeParsed = true;
+    T = ParseTypeName(&TypeRange);
     break;
 
   case tok::identifier:
+    if(AttrName->isStr("vec_type_hint")) {
+      TypeParsed = true;
+      T = ParseTypeName(&TypeRange);
+      break;
+    }
     ParmName = Tok.getIdentifierInfo();
     ParmLoc = ConsumeToken();
     break;
@@ -233,8 +241,9 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
   }
 
   ExprVector ArgExprs(Actions);
+  bool isInvalid = false;
 
-  if (!BuiltinType &&
+  if (!BuiltinType && !AttrName->isStr("vec_type_hint") &&
       (ParmLoc.isValid() ? Tok.is(tok::comma) : Tok.isNot(tok::r_paren))) {
     // Eat the comma.
     if (ParmLoc.isValid())
@@ -270,9 +279,24 @@ void Parser::ParseGNUAttributeArgs(IdentifierInfo *AttrName,
       SkipUntil(tok::r_paren, false, true); // skip until ')'
     }
   }
+  else if(AttrName->isStr("vec_type_hint")) {
+    if(T.get() && !T.isInvalid()) {
+      ExprResult ArgExpr = Actions.ActOnDummyTypeExpr(T.get());
+      ArgExprs.push_back(ArgExpr.release());
+    } else {
+      if (Tok.is(tok::identifier)) ConsumeToken();
+      if (TypeParsed) isInvalid = true;
+    }
+
+    if (!Tok.is(tok::r_paren)) {
+      Diag(Tok, diag::err_expected_rparen);
+      ConsumeToken();
+      SkipUntil(tok::r_paren, false, true);
+    }
+  }
 
   SourceLocation RParen = Tok.getLocation();
-  if (!ExpectAndConsume(tok::r_paren, diag::err_expected_rparen)) {
+  if (!ExpectAndConsume(tok::r_paren, diag::err_expected_rparen) && !isInvalid) {
     AttributeList *attr =
       Attrs.addNew(AttrName, SourceRange(AttrNameLoc, RParen), 0, AttrNameLoc,
                    ParmName, ParmLoc, ArgExprs.take(), ArgExprs.size());
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index 19ac616..518f213 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -2318,11 +2318,14 @@ static void handleWeakImportAttr(Sema &S, Decl *D, const AttributeList &Attr) {
   D->addAttr(::new (S.Context) WeakImportAttr(Attr.getRange(), S.Context));
 }
 
-static void handleReqdWorkGroupSize(Sema &S, Decl *D,
-                                    const AttributeList &Attr) {
+// Handles reqd_work_group_size and work_group_size_hint.
+static void handleWorkGroupSize(Sema &S, Decl *D,
+				const AttributeList &Attr) {
+  assert(Attr.getKind() == AttributeList::AT_reqd_work_group_size 
+      || Attr.getKind() == AttributeList::AT_work_group_size_hint);
+
   // Attribute has 3 arguments.
-  if (!checkAttributeNumArgs(S, Attr, 3))
-    return;
+  if (!checkAttributeNumArgs(S, Attr, 3)) return;
 
   unsigned WGSize[3];
   for (unsigned i = 0; i < 3; ++i) {
@@ -2331,14 +2334,71 @@ static void handleReqdWorkGroupSize(Sema &S, Decl *D,
     if (E->isTypeDependent() || E->isValueDependent() ||
         !E->isIntegerConstantExpr(ArgNum, S.Context)) {
       S.Diag(Attr.getLoc(), diag::err_attribute_argument_not_int)
-        << "reqd_work_group_size" << E->getSourceRange();
+        << Attr.getName()->getName() << E->getSourceRange();
       return;
     }
     WGSize[i] = (unsigned) ArgNum.getZExtValue();
   }
-  D->addAttr(::new (S.Context) ReqdWorkGroupSizeAttr(Attr.getRange(), S.Context,
-                                                     WGSize[0], WGSize[1],
-                                                     WGSize[2]));
+
+  if (Attr.getKind() == AttributeList::AT_reqd_work_group_size
+    && D->hasAttr<ReqdWorkGroupSizeAttr>()) {
+      ReqdWorkGroupSizeAttr *A = D->getAttr<ReqdWorkGroupSizeAttr>();
+      if (!(A->getXDim() == WGSize[0] &&
+            A->getYDim() == WGSize[1] &&
+            A->getZDim() == WGSize[2])) {
+        S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) <<
+          Attr.getName();
+      }
+  }
+
+  if (Attr.getKind() == AttributeList::AT_work_group_size_hint
+    && D->hasAttr<WorkGroupSizeHintAttr>()) {
+      WorkGroupSizeHintAttr *A = D->getAttr<WorkGroupSizeHintAttr>();
+      if (!(A->getXDim() == WGSize[0] &&
+            A->getYDim() == WGSize[1] &&
+            A->getZDim() == WGSize[2])) {
+        S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) <<
+          Attr.getName();
+      }
+  }
+
+  if (Attr.getKind() == AttributeList::AT_reqd_work_group_size)
+    D->addAttr(::new (S.Context)
+                 ReqdWorkGroupSizeAttr(Attr.getRange(), S.Context,
+                                       WGSize[0], WGSize[1], WGSize[2]));
+  else
+    D->addAttr(::new (S.Context)
+                 WorkGroupSizeHintAttr(Attr.getRange(), S.Context,
+                                       WGSize[0], WGSize[1], WGSize[2]));
+}
+
+static void handleVecTypeHint(Sema &S, Decl *D,
+                              const AttributeList &Attr) {
+  // Attribute has exactly 1 argument.
+  if (!checkAttributeNumArgs(S, Attr, 1)) return;
+
+  QualType QType = Attr.getArg(0)->getType();
+
+  if (!(QType->isExtVectorType() ||
+        QType->isIntegralType(S.getASTContext()) ||
+        QType->isFloatingType()) ||
+       QType->isBooleanType()) {
+    S.Diag(Attr.getLoc(), diag::err_attribute_argument_invalid_type) << QType;
+    return;
+  }
+
+  if (Attr.getKind() == AttributeList::AT_vec_type_hint
+    && D->hasAttr<VecTypeHintAttr>()) {
+      VecTypeHintAttr *A = D->getAttr<VecTypeHintAttr>();
+      if (A->getTypeHint() != QType) {
+        S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute) <<
+          Attr.getName();
+        return;
+      }
+  }
+
+  D->addAttr(::new (S.Context) VecTypeHintAttr(Attr.getLoc(), S.Context,
+                                                     QType, Attr.getLoc()));
 }
 
 SectionAttr *Sema::mergeSectionAttr(Decl *D, SourceRange Range,
@@ -3998,8 +4058,16 @@ static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D,
   case AttributeList::AT_cf_returns_retained:
     handleNSReturnsRetainedAttr(S, D, Attr); break;
 
+  case AttributeList::AT_work_group_size_hint:
   case AttributeList::AT_reqd_work_group_size:
-    handleReqdWorkGroupSize(S, D, Attr); break;
+    handleWorkGroupSize(S, D, Attr); break;
+
+  case AttributeList::AT_vec_type_hint:
+    handleVecTypeHint(S, D, Attr); break;
+
+  case AttributeList::AT_endian:
+    // Ignored.
+    break;
 
   case AttributeList::AT_init_priority: 
       handleInitPriorityAttr(S, D, Attr); break;
diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp
index d2377f5..9246ed1 100644
--- a/lib/Sema/SemaExpr.cpp
+++ b/lib/Sema/SemaExpr.cpp
@@ -3772,6 +3772,11 @@ ExprResult Sema::ActOnAsTypeExpr(Expr *E, ParsedType ParsedDestTy,
                RParenLoc));
 }
 
+ExprResult Sema::ActOnDummyTypeExpr(ParsedType ParsedTy) {
+  QualType type = GetTypeFromParser(ParsedTy);
+  return Owned(new (Context) DummyTypeExpr(type));
+}
+
 /// BuildResolvedCallExpr - Build a call to a resolved expression,
 /// i.e. an expression not of \p OverloadTy.  The expression should
 /// unary-convert to an expression of function-pointer or
diff --git a/test/CodeGenOpenCL/kernel-attributes.cl b/test/CodeGenOpenCL/kernel-attributes.cl
new file mode 100644
index 0000000..5503284
--- /dev/null
+++ b/test/CodeGenOpenCL/kernel-attributes.cl
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -emit-llvm -O0 -o - %s | FileCheck %s
+
+typedef float float4 __attribute__((ext_vector_type(4)));
+
+kernel __attribute__((vec_type_hint(int))) __attribute__((reqd_work_group_size(1,2,4))) void kernel1(int a) {}
+
+kernel __attribute__((vec_type_hint(float4))) __attribute__((work_group_size_hint(8,16,32))) void kernel2(float4 a) {}
+
+// CHECK: opencl.kernels = !{[[MDNODE0:![0-9]+]], [[MDNODE3:![0-9]+]]}
+
+// CHECK: [[MDNODE0]] = metadata !{void (i32)* @kernel1, metadata [[MDNODE1:![0-9]+]], metadata [[MDNODE2:![0-9]+]]}
+// CHECK: [[MDNODE1]] = metadata !{metadata !"vec_type_hint", i32 undef, i32 1}
+// CHECK: [[MDNODE2]] = metadata !{metadata !"reqd_work_group_size", i32 1, i32 2, i32 4}
+// CHECK: [[MDNODE3]] = metadata !{void (<4 x float>)* @kernel2, metadata [[MDNODE4:![0-9]+]], metadata [[MDNODE5:![0-9]+]]}
+// CHECK: [[MDNODE4]] = metadata !{metadata !"vec_type_hint", <4 x float> undef, i32 0}
+// CHECK: [[MDNODE5]] = metadata !{metadata !"work_group_size_hint", i32 8, i32 16, i32 32}
diff --git a/test/SemaOpenCL/kernel-attributes-invalid.cl b/test/SemaOpenCL/kernel-attributes-invalid.cl
new file mode 100644
index 0000000..97da6e7
--- /dev/null
+++ b/test/SemaOpenCL/kernel-attributes-invalid.cl
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -verify %s 
+
+kernel __attribute__((vec_type_hint(void))) void kernel1() {} //expected-error{{expecting a vector or vectorizable scalar type}}
+
+kernel __attribute__((work_group_size_hint(8,16,32,4))) void kernel2() {} //expected-error{{attribute requires exactly 3 arguments}}
+
+
+kernel __attribute__((vec_type_hint(int))) __attribute__((vec_type_hint(float))) void kernel3() {} //expected-warning{{attribute 'vec_type_hint' is already applied with different parameters}}
+
+kernel __attribute__((work_group_size_hint(1,2,3))) __attribute__((work_group_size_hint(3,2,1))) void kernel4() {}  //expected-warning{{attribute 'work_group_size_hint' is already applied with different parameters}}
