erik.pilkington updated this revision to Diff 209603.
erik.pilkington marked 2 inline comments as done.
erik.pilkington added a comment.

Address review comments.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D64600/new/

https://reviews.llvm.org/D64600

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/AST/TypePrinter.cpp
  clang/lib/CodeGen/CGExpr.cpp
  clang/lib/CodeGen/CGExprScalar.cpp
  clang/lib/CodeGen/CodeGenFunction.h
  clang/lib/Sema/SemaType.cpp
  clang/test/CodeGen/clamping-integral-bool.m
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/clamping-integral-bool.m

Index: clang/test/Sema/clamping-integral-bool.m
===================================================================
--- /dev/null
+++ clang/test/Sema/clamping-integral-bool.m
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 %s -verify -fsyntax-only
+
+typedef __attribute__((clamping_integral_bool)) signed char BOOL;
+typedef __attribute__((clamping_integral_bool)) unsigned char BOOL2;
+typedef __attribute__((clamping_integral_bool)) int BOOL3;
+
+// expected-error@+1 {{clamping_integral_bool attribute must appertain to an integral type}}
+typedef __attribute__((clamping_integral_bool)) float BOOL4;
+
+// expected-error@+1 {{'clamping_integral_bool' attribute takes no arguments}}
+typedef __attribute__((clamping_integral_bool(1))) signed char BOOL5;
+
+// expected-warning@+1 {{'clamping_integral_bool' attribute only applies to typedefs}}
+__attribute__((clamping_integral_bool)) signed char x;
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -35,6 +35,7 @@
 // CHECK-NEXT: Callback (SubjectMatchRule_function)
 // CHECK-NEXT: Capability (SubjectMatchRule_record, SubjectMatchRule_type_alias)
 // CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function)
+// CHECK-NEXT: ClampingIntegralBool (SubjectMatchRule_type_alias)
 // CHECK-NEXT: Cold (SubjectMatchRule_function)
 // CHECK-NEXT: Common (SubjectMatchRule_variable)
 // CHECK-NEXT: Constructor (SubjectMatchRule_function)
Index: clang/test/CodeGen/clamping-integral-bool.m
===================================================================
--- /dev/null
+++ clang/test/CodeGen/clamping-integral-bool.m
@@ -0,0 +1,160 @@
+// RUN: %clang_cc1 %s -triple x86_64-apple-macosx10.14 -emit-llvm -disable-llvm-passes -o - | FileCheck %s
+
+typedef __attribute__((clamping_integral_bool)) signed char BOOL;
+
+BOOL b;
+
+void store() {
+  // CHECK-LABEL: define void @store
+
+  b = 2;
+  // CHECK: store i8 1, i8* @b, align 1
+
+  b = 1;
+  // CHECK: store i8 1, i8* @b, align 1
+
+  b = -1;
+  // CHECK: store i8 1, i8* @b, align 1
+
+  b = 0;
+  // CHECK: store i8 0, i8* @b, align 1
+
+  b = 256;
+  // CHECK: store i8 1, i8* @b, align 1
+
+  int unknown_value;
+  b = unknown_value;
+
+  // CHECK: [[CMPNE:%.*]] = icmp ne i32 %{{.*}}, 0
+  // CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMPNE]] to i8
+}
+
+void load() {
+  // CHECK-LABEL: define void @load
+  int load = b;
+
+  // CHECK: [[LOAD:%.*]] = load i8, i8* @b, align 1
+  // CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 %0, 0
+  // CHECK-NEXT: zext i1 [[CMP]] to i8
+}
+
+void cast() {
+  // CHECK-LABEL: define void @cast
+  int i;
+  float f;
+  long double ld;
+  int* p;
+  _Bool real_bool;
+
+  (BOOL)i;
+  // CHECK: [[CMP:%.*]] = icmp ne i32 %{{.*}}, 0
+  // CHECK-NEXT: zext i1 [[CMP]] to i8
+
+  (BOOL)f;
+  // CHECK: [[CMP:%.*]] = fcmp une float %{{.*}}, 0.000000e+00
+  // CHECK-NEXT: zext i1 [[CMP]] to i8
+
+  (BOOL)ld;
+  // CHECK: [[CMP:%.*]] = fcmp une x86_fp80 %{{.*}}, 0xK00000000000000000000
+  // CHECK-NEXT: zext i1 [[CMP]] to i8
+
+  (BOOL)p;
+  // CHECK: [[CMP:%.*]] = icmp ne i32* %{{.*}}, null
+  // CHECK-NEXT: zext i1 [[CMP]] to i8
+
+  (BOOL)real_bool;
+  // CHECK: [[CMP:%.*]] = icmp ne i1 %{{.*}}, false
+  // CHECK-NEXT: zext i1 [[CMP]] to i8
+}
+
+void bits() {
+  // CHECK-LABEL: define void @bits
+  struct bit_field {
+    BOOL b : 1;
+  };
+
+  struct bit_field bf;
+
+  bf.b = 1;
+  // CHECK: [[SET_BIT:%.*]] = or i8 %{{.*}}, 1
+  // CHECK-NEXT: store i8 [[SET_BIT]]
+
+  bf.b = 2;
+  // CHECK: [[SET_BIT:%.*]] = or i8 %{{.*}}, 1
+  // CHECK-NEXT: store i8 [[SET_BIT]]
+
+  bf.b = 0;
+  // CHECK: [[ZERO_BIT:%.*]] = and i8 %{{.*}}, -2
+  // CHECK-NEXT: store i8 [[ZERO_BIT]]
+
+  bf.b = -1;
+  // CHECK: [[SET_BIT:%.*]] = or i8 %{{.*}}, 1
+  // CHECK-NEXT: store i8 [[SET_BIT]]
+
+  (int)bf.b;
+  // CHECK: shl i8 %{{.*}}, 7
+  // CHECK-NEXT: [[LOAD:%.*]] = ashr i8 %{{.*}}, 7
+  // CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[LOAD]], 0
+  // CHECK-NEXT: zext i1 [[CMP]] to i8
+  // CHECK-NOT: icmp
+
+  (BOOL)bf.b;
+  // CHECK: shl i8 %{{.*}}, 7
+  // CHECK-NEXT: [[LOAD:%.*]] = ashr i8 %{{.*}}, 7
+  // CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[LOAD]], 0
+  // CHECK-NEXT: zext i1 [[CMP]] to i8
+  // CHECK-NOT: icmp
+}
+
+@interface HasProp
+@property BOOL b;
+@property int i;
+@end
+
+void prop(HasProp *p) {
+  // CHECK-LABEL: define void @prop
+  p.b = 43;
+  // CHECK: call void {{.*}} @objc_msgSend {{.*}}(i8* {{.*}}, i8*{{.*}}, i8 signext 1)
+  p.b = 0;
+  // CHECK: call void {{.*}} @objc_msgSend {{.*}}(i8* {{.*}}, i8*{{.*}}, i8 signext 0)
+  p.b = -1;
+  // CHECK: call void {{.*}} @objc_msgSend {{.*}}(i8* {{.*}}, i8*{{.*}}, i8 signext 1)
+  p.b = 1;
+  // CHECK: call void {{.*}} @objc_msgSend {{.*}}(i8* {{.*}}, i8*{{.*}}, i8 signext 1)
+
+  BOOL b;
+
+  p.i = b;
+  // CHECK: [[CMP:%.*]] = icmp ne i8 %{{.*}}, 0
+  // CHECK-NEXT: [[ZEXT:%.*]] = zext i1 [[CMP]] to i8
+  // CHECK-NEXT: [[SEXT:%.*]] = sext i8 [[ZEXT]] to i32
+  // CHECK: call void {{.*}} @objc_msgSend {{.*}}(i8* {{.*}}, i8*{{.*}}, i32 [[SEXT]])
+}
+
+typedef __attribute__((clamping_integral_bool)) int int_bool;
+
+int_bool ib;
+
+void int_bool_test() {
+  // CHECK-LABEL: define void @int_bool_test
+  int *p;
+
+  ib = 3;
+  // CHECK: store i32 1, i32* @ib
+
+  ib = 0;
+  // CHECK: store i32 0, i32* @ib
+
+  ib = 1;
+  // CHECK: store i32 1, i32* @ib
+
+  ib = 23.f;
+  // CHECK: store i32 1, i32* @ib
+
+  ib = 0.f;
+  // CHECK: store i32 0, i32* @ib
+
+  int load = ib;
+  // CHECK: [[CMP:%.*]] = icmp ne i32 %{{.*}}, 0
+  // CHECK-NEXT: zext i1 [[CMP]] to i32
+}
Index: clang/lib/Sema/SemaType.cpp
===================================================================
--- clang/lib/Sema/SemaType.cpp
+++ clang/lib/Sema/SemaType.cpp
@@ -7485,6 +7485,20 @@
   }
 }
 
+static void handleClampingIntegralBoolAttr(TypeProcessingState &State,
+                                           QualType &CurType,
+                                           ParsedAttr &Attr) {
+  if (!CurType->isIntegralType(State.getSema().getASTContext())) {
+    State.getSema().Diag(Attr.getLoc(),
+                         diag::err_clamping_integral_bool_bad_type);
+    Attr.setInvalid();
+    return;
+  }
+
+  auto *ClampAttr = createSimpleAttr<ClampingIntegralBoolAttr>(
+      State.getSema().getASTContext(), Attr);
+  CurType = State.getAttributedType(ClampAttr, CurType, CurType);
+}
 
 static void processTypeAttrs(TypeProcessingState &state, QualType &type,
                              TypeAttrLocation TAL,
@@ -7664,6 +7678,11 @@
         attr.setInvalid();
       break;
 
+    case ParsedAttr::AT_ClampingIntegralBool:
+      handleClampingIntegralBoolAttr(state, type, attr);
+      attr.setUsedAsTypeAttr();
+      break;
+
     case ParsedAttr::AT_NoThrow:
     // Exception Specifications aren't generally supported in C mode throughout
     // clang, so revert to attribute-based handling for C.
Index: clang/lib/CodeGen/CodeGenFunction.h
===================================================================
--- clang/lib/CodeGen/CodeGenFunction.h
+++ clang/lib/CodeGen/CodeGenFunction.h
@@ -3396,6 +3396,9 @@
                         const llvm::function_ref<RValue(RValue)> &UpdateOp,
                         bool IsVolatile);
 
+  /// Emit code to "clamp" an integer into {0,1}.
+  llvm::Value *EmitClampIntegerToBool(llvm::Value *);
+
   /// EmitToMemory - Change a scalar value from its value
   /// representation to its in-memory representation.
   llvm::Value *EmitToMemory(llvm::Value *Value, QualType Ty);
Index: clang/lib/CodeGen/CGExprScalar.cpp
===================================================================
--- clang/lib/CodeGen/CGExprScalar.cpp
+++ clang/lib/CodeGen/CGExprScalar.cpp
@@ -1212,6 +1212,11 @@
 
   llvm::Type *DstTy = ConvertType(DstType);
 
+  if (NoncanonicalDstType->hasAttr(attr::ClampingIntegralBool)) {
+    Value *ToBool = EmitConversionToBool(Src, SrcType);
+    return Builder.CreateZExt(ToBool, DstTy, "BOOL.clamp");
+  }
+
   // Cast from half through float if half isn't a native type.
   if (SrcType->isHalfType() && !CGF.getContext().getLangOpts().NativeHalfType) {
     // Cast to FP using the intrinsic if the half type itself isn't supported.
@@ -2174,6 +2179,11 @@
     assert(!DestTy->isBooleanType() && "bool should use PointerToBool");
     auto *PtrExpr = Visit(E);
 
+    if (DestTy->hasAttr(attr::ClampingIntegralBool)) {
+      llvm::Value *ToBool = EmitPointerToBoolConversion(PtrExpr, E->getType());
+      return Builder.CreateZExt(ToBool, ConvertType(DestTy), "clamped.bool");
+    }
+
     if (CGF.CGM.getCodeGenOpts().StrictVTablePointers) {
       const QualType SrcType = E->getType();
 
Index: clang/lib/CodeGen/CGExpr.cpp
===================================================================
--- clang/lib/CodeGen/CGExpr.cpp
+++ clang/lib/CodeGen/CGExpr.cpp
@@ -1687,6 +1687,18 @@
   return EmitFromMemory(Load, Ty);
 }
 
+llvm::Value *CodeGenFunction::EmitClampIntegerToBool(llvm::Value *Value) {
+  assert(Value->getType()->isIntegerTy() && "Can't clamp a non-integral type!");
+
+  // Avoid double-clamping if we know that Value is already boolean.
+  if (auto *ZExt = dyn_cast<llvm::ZExtInst>(Value))
+    if (ZExt->getOperand(0)->getType()->isIntegerTy(1))
+      return Value;
+
+  llvm::Value *NonZero = Builder.CreateIsNotNull(Value);
+  return Builder.CreateZExt(NonZero, Value->getType(), "BOOL.clamp");
+}
+
 llvm::Value *CodeGenFunction::EmitToMemory(llvm::Value *Value, QualType Ty) {
   // Bool has a different representation in memory than in registers.
   if (hasBooleanRepresentation(Ty)) {
@@ -1698,6 +1710,9 @@
            "wrong value rep of bool");
   }
 
+  if (Ty->hasAttr(attr::ClampingIntegralBool))
+    Value = EmitClampIntegerToBool(Value);
+
   return Value;
 }
 
@@ -1709,6 +1724,9 @@
     return Builder.CreateTrunc(Value, Builder.getInt1Ty(), "tobool");
   }
 
+  if (Ty->hasAttr(attr::ClampingIntegralBool))
+    Value = EmitClampIntegerToBool(Value);
+
   return Value;
 }
 
@@ -1843,6 +1861,8 @@
   }
   Val = Builder.CreateIntCast(Val, ResLTy, Info.IsSigned, "bf.cast");
   EmitScalarRangeCheck(Val, LV.getType(), Loc);
+  if (LV.getType()->hasAttr(attr::ClampingIntegralBool))
+    Val = EmitClampIntegerToBool(Val);
   return RValue::get(Val);
 }
 
Index: clang/lib/AST/TypePrinter.cpp
===================================================================
--- clang/lib/AST/TypePrinter.cpp
+++ clang/lib/AST/TypePrinter.cpp
@@ -1519,6 +1519,10 @@
     OS << "ns_returns_retained";
     break;
 
+  case attr::ClampingIntegralBool:
+    OS << "clamping_integral_bool";
+    break;
+
   // FIXME: When Sema learns to form this AttributedType, avoid printing the
   // attribute again in printFunctionProtoAfter.
   case attr::AnyX86NoCfCheck: OS << "nocf_check"; break;
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2708,6 +2708,8 @@
 def err_swift_abi_parameter_wrong_type : Error<
   "'%0' parameter must have pointer%select{| to unqualified pointer}1 type; "
   "type here is %2">;
+def err_clamping_integral_bool_bad_type : Error<
+  "clamping_integral_bool attribute must appertain to an integral type">;
 
 def err_attribute_argument_invalid : Error<
   "%0 attribute argument is invalid: %select{max must be 0 since min is 0|"
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -4146,6 +4146,24 @@
 When compiled without ``-fobjc-arc``, this attribute is ignored.
 }]; }
 
+def ClampingIntegralBoolDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``clamping_integral_bool`` attribute appertains only to typedefs that alias
+an integral type. Its meant to be used to annotate custom boolean types in
+languages without a native bool type, namely (but not necessarily) in
+Objective-C on macOS. When this attribute is used, stores, loads, and casts to
+the boolean type become "clamped", so non-zero values become YES, and zero
+values become NO.
+
+This attribute is just sugar on the underlying type. If used with a language
+feature that canonicalizes the type (such as C++ templates or auto) the sugar is
+stripped away, and with it the clamping behaviour. For this reason, its
+preferable to update code to perform the conversion manually, like:
+`some_value ? YES : NO`.
+  }];
+}
+
 def MIGConventionDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
@@ -4194,4 +4212,4 @@
 not initialized on device side. It has internal linkage and is initialized by
 the initializer on host side.
   }];
-}
\ No newline at end of file
+}
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -3274,3 +3274,9 @@
   let Subjects = SubjectList<[NonParmVar, Function, Block, ObjCMethod]>;
   let Documentation = [ObjCExternallyRetainedDocs];
 }
+
+def ClampingIntegralBool : TypeAttr {
+  let Spellings = [Clang<"clamping_integral_bool">];
+  let Subjects = SubjectList<[TypedefName]>;
+  let Documentation = [ClampingIntegralBoolDocs];
+}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to