wmi updated this revision to Diff 116232.
wmi added a comment.

Changes following the discussion:

- Put the bitfield split logic under an option and off by default.
- When sanitizer is enabled, the option for bitfield split will be ignored and 
a warning message will be emitted.

In addition, a test is added.


Repository:
  rL LLVM

https://reviews.llvm.org/D36562

Files:
  include/clang/Basic/DiagnosticDriverKinds.td
  include/clang/Driver/Options.td
  include/clang/Frontend/CodeGenOptions.def
  lib/CodeGen/CGExpr.cpp
  lib/Driver/ToolChains/Clang.cpp
  lib/Frontend/CompilerInvocation.cpp
  test/CodeGenCXX/bitfield-split.cpp

Index: test/CodeGenCXX/bitfield-split.cpp
===================================================================
--- test/CodeGenCXX/bitfield-split.cpp
+++ test/CodeGenCXX/bitfield-split.cpp
@@ -0,0 +1,161 @@
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsplit-bitfields -emit-llvm \
+// RUN:  -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsplit-bitfields -emit-llvm \
+// RUN:  -fsanitize=address -o - %s | FileCheck %s --check-prefix=SANITIZE
+// Check -fsplit-bitfields will be ignored since sanitizer is enabled.
+
+struct S1 {
+  unsigned f1:2;
+  unsigned f2:6;
+  unsigned f3:8;
+  unsigned f4:4;
+  unsigned f5:8;
+};
+
+S1 a1;
+unsigned read8_1() {
+  // CHECK-LABEL: @_Z7read8_1v
+  // CHECK: %bf.load = load i8, i8* getelementptr (i8, i8* bitcast (%struct.S1* @a1 to i8*), i32 1), align 1
+  // CHECK-NEXT: %bf.cast = zext i8 %bf.load to i32
+  // CHECK-NEXT: ret i32 %bf.cast
+  // SANITIZE-LABEL: @_Z7read8_1v
+  // SANITIZE: %bf.load = load i32, i32* getelementptr inbounds {{.*}}, align 4
+  // SANITIZE: %bf.lshr = lshr i32 %bf.load, 8
+  // SANITIZE: %bf.clear = and i32 %bf.lshr, 255
+  // SANITIZE: ret i32 %bf.clear
+  return a1.f3;
+}
+void write8_1() {
+  // CHECK-LABEL: @_Z8write8_1v
+  // CHECK: store i8 3, i8* getelementptr (i8, i8* bitcast (%struct.S1* @a1 to i8*), i32 1), align 1
+  // CHECK-NEXT: ret void
+  // SANITIZE-LABEL: @_Z8write8_1v
+  // SANITIZE: %bf.load = load i32, i32* getelementptr inbounds {{.*}}, align 4
+  // SANITIZE-NEXT: %bf.clear = and i32 %bf.load, -65281
+  // SANITIZE-NEXT: %bf.set = or i32 %bf.clear, 768
+  // SANITIZE-NEXT: store i32 %bf.set, i32* getelementptr inbounds {{.*}}, align 4
+  // SANITIZE-NEXT: ret void
+  a1.f3 = 3;
+}
+
+unsigned read8_2() {
+  // CHECK-LABEL: @_Z7read8_2v
+  // CHECK: %bf.load = load i32, i32* getelementptr inbounds (%struct.S1, %struct.S1* @a1, i32 0, i32 0), align 4
+  // CHECK-NEXT: %bf.lshr = lshr i32 %bf.load, 20
+  // CHECK-NEXT: %bf.clear = and i32 %bf.lshr, 255
+  // CHECK-NEXT: ret i32 %bf.clear
+  // SANITIZE-LABEL: @_Z7read8_2v
+  // SANITIZE: %bf.load = load i32, i32* getelementptr inbounds {{.*}}, align 4
+  // SANITIZE-NEXT: %bf.lshr = lshr i32 %bf.load, 20
+  // SANITIZE-NEXT: %bf.clear = and i32 %bf.lshr, 255
+  // SANITIZE-NEXT: ret i32 %bf.clear
+  return a1.f5;
+}
+void write8_2() {
+  // CHECK-LABEL: @_Z8write8_2v
+  // CHECK: %bf.load = load i32, i32* getelementptr inbounds (%struct.S1, %struct.S1* @a1, i32 0, i32 0), align 4
+  // CHECK-NEXT: %bf.clear = and i32 %bf.load, -267386881
+  // CHECK-NEXT: %bf.set = or i32 %bf.clear, 3145728
+  // CHECK-NEXT: store i32 %bf.set, i32* getelementptr inbounds (%struct.S1, %struct.S1* @a1, i32 0, i32 0), align 4
+  // CHECK-NEXT: ret void
+  // SANITIZE-LABEL: @_Z8write8_2v
+  // SANITIZE: %bf.load = load i32, i32* getelementptr inbounds {{.*}}, align 4
+  // SANITIZE-NEXT: %bf.clear = and i32 %bf.load, -267386881
+  // SANITIZE-NEXT: %bf.set = or i32 %bf.clear, 3145728
+  // SANITIZE-NEXT: store i32 %bf.set, i32* getelementptr inbounds {{.*}}, align 4
+  // SANITIZE-NEXT: ret void
+  a1.f5 = 3;
+}
+
+struct S2 {
+  unsigned long f1:16;
+  unsigned long f2:16;
+  unsigned long f3:6;
+};
+
+S2 a2;
+unsigned read16_1() {
+  // CHECK-LABEL: @_Z8read16_1v
+  // CHECK: %bf.load = load i16, i16* bitcast (%struct.S2* @a2 to i16*), align 2
+  // CHECK-NEXT: %bf.cast = zext i16 %bf.load to i64
+  // CHECK-NEXT: %conv = trunc i64 %bf.cast to i32
+  // CHECK-NEXT: ret i32 %conv
+  // SANITIZE-LABEL: @_Z8read16_1v
+  // SANITIZE: %bf.load = load i64, i64* bitcast {{.*}}, align 8
+  // SANITIZE-NEXT: %bf.clear = and i64 %bf.load, 65535
+  // SANITIZE-NEXT: %conv = trunc i64 %bf.clear to i32
+  // SANITIZE-NEXT: ret i32 %conv
+  return a2.f1;
+}
+unsigned read16_2() {
+  // CHECK-LABEL: @_Z8read16_2v
+  // CHECK: %bf.load = load i16, i16* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.S2* @a2 to i8*), i32 2) to i16*), align 2
+  // CHECK-NEXT: %bf.cast = zext i16 %bf.load to i64
+  // CHECK-NEXT: %conv = trunc i64 %bf.cast to i32
+  // CHECK-NEXT: ret i32 %conv
+  // SANITIZE-LABEL: @_Z8read16_2v
+  // SANITIZE: %bf.load = load i64, i64* bitcast {{.*}}, align 8
+  // SANITIZE-NEXT: %bf.lshr = lshr i64 %bf.load, 16
+  // SANITIZE-NEXT: %bf.clear = and i64 %bf.lshr, 65535
+  // SANITIZE-NEXT: %conv = trunc i64 %bf.clear to i32
+  // SANITIZE-NEXT: ret i32 %conv
+  return a2.f2;
+}
+
+void write16_1() {
+  // CHECK-LABEL: @_Z9write16_1v
+  // CHECK: store i16 5, i16* bitcast (%struct.S2* @a2 to i16*), align 2
+  // CHECK-NEXT: ret void
+  // SANITIZE-LABEL: @_Z9write16_1v
+  // SANITIZE: %bf.load = load i64, i64* bitcast {{.*}}, align 8
+  // SANITIZE-NEXT: %bf.clear = and i64 %bf.load, -65536
+  // SANITIZE-NEXT: %bf.set = or i64 %bf.clear, 5
+  // SANITIZE-NEXT: store i64 %bf.set, i64* bitcast {{.*}}, align 8
+  // SANITIZE-NEXT: ret void
+  a2.f1 = 5;
+}
+void write16_2() {
+  // CHECK-LABEL: @_Z9write16_2v
+  // CHECK: store i16 5, i16* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.S2* @a2 to i8*), i32 2) to i16*), align 2
+  // CHECK-NEXT: ret void
+  // SANITIZE-LABEL: @_Z9write16_2v
+  // SANITIZE: %bf.load = load i64, i64* bitcast {{.*}}, align 8
+  // SANITIZE-NEXT: %bf.clear = and i64 %bf.load, -4294901761
+  // SANITIZE-NEXT: %bf.set = or i64 %bf.clear, 327680
+  // SANITIZE-NEXT: store i64 %bf.set, i64* bitcast {{.*}}, align 8
+  // SANITIZE-NEXT: ret void
+  a2.f2 = 5;
+}
+
+struct S3 {
+  unsigned long f1:14;
+  unsigned long f2:18;
+  unsigned long f3:32;
+};
+
+S3 a3;
+unsigned read32_1() {
+  // CHECK-LABEL: @_Z8read32_1v
+  // CHECK: %bf.load = load i32, i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.S3* @a3 to i8*), i32 4) to i32*), align 4
+  // CHECK-NEXT: %bf.cast = zext i32 %bf.load to i64
+  // CHECK-NEXT: %conv = trunc i64 %bf.cast to i32
+  // CHECK-NEXT: ret i32 %conv
+  // SANITIZE-LABEL: @_Z8read32_1v
+  // SANITIZE: %bf.load = load i64, i64* getelementptr inbounds {{.*}}, align 8
+  // SANITIZE-NEXT: %bf.lshr = lshr i64 %bf.load, 32
+  // SANITIZE-NEXT: %conv = trunc i64 %bf.lshr to i32
+  // SANITIZE-NEXT: ret i32 %conv
+  return a3.f3;
+}
+void write32_1() {
+  // CHECK-LABEL: @_Z9write32_1v
+  // CHECK: store i32 5, i32* bitcast (i8* getelementptr (i8, i8* bitcast (%struct.S3* @a3 to i8*), i32 4) to i32*), align 4
+  // CHECK-NEXT: ret void
+  // SANITIZE-LABEL: @_Z9write32_1v
+  // SANITIZE: %bf.load = load i64, i64* getelementptr inbounds {{.*}}, align 8
+  // SANITIZE-NEXT: %bf.clear = and i64 %bf.load, 4294967295
+  // SANITIZE-NEXT: %bf.set = or i64 %bf.clear, 21474836480
+  // SANITIZE-NEXT: store i64 %bf.set, i64* getelementptr inbounds {{.*}}, align 8
+  // SANITIZE-NEXT: ret void
+  a3.f3 = 5;
+}
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -545,6 +545,8 @@
     OPT_fuse_register_sized_bitfield_access);
   Opts.RelaxedAliasing = Args.hasArg(OPT_relaxed_aliasing);
   Opts.StructPathTBAA = !Args.hasArg(OPT_no_struct_path_tbaa);
+  Opts.SplitBitfields =
+      Args.hasFlag(OPT_fsplit_bitfields, OPT_fno_split_bitfields, false);
   Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags);
   Opts.MergeAllConstants = !Args.hasArg(OPT_fno_merge_all_constants);
   Opts.NoCommon = Args.hasArg(OPT_fno_common);
@@ -2729,6 +2731,13 @@
   if (Arch == llvm::Triple::spir || Arch == llvm::Triple::spir64) {
     Res.getDiagnosticOpts().Warnings.push_back("spir-compat");
   }
+
+  // If sanitizer is enabled, disable OPT_fsplit_bitfields
+  if (Res.getCodeGenOpts().SplitBitfields &&
+      !Res.getLangOpts()->Sanitize.empty()) {
+    Res.getCodeGenOpts().SplitBitfields = false;
+    Diags.Report(diag::warn_drv_split_bitfields_ignored);
+  }
   return Success;
 }
 
Index: lib/Driver/ToolChains/Clang.cpp
===================================================================
--- lib/Driver/ToolChains/Clang.cpp
+++ lib/Driver/ToolChains/Clang.cpp
@@ -2406,6 +2406,9 @@
                     options::OPT_fno_optimize_sibling_calls))
     CmdArgs.push_back("-mdisable-tail-calls");
 
+  Args.AddLastArg(CmdArgs, options::OPT_fsplit_bitfields,
+                  options::OPT_fno_split_bitfields);
+
   // Handle segmented stacks.
   if (Args.hasArg(options::OPT_fsplit_stack))
     CmdArgs.push_back("-split-stacks");
Index: lib/CodeGen/CGExpr.cpp
===================================================================
--- lib/CodeGen/CGExpr.cpp
+++ lib/CodeGen/CGExpr.cpp
@@ -1674,30 +1674,76 @@
   return EmitLoadOfBitfieldLValue(LV, Loc);
 }
 
+/// Check if current Field is better to be accessed separately. When current
+/// field has legal integer width, and its bitfield offset is naturally
+/// aligned, it is better to access the bitfield like a separate integer var.
+static bool IsSeparatableBitfield(const CGBitFieldInfo &Info,
+                                  const llvm::DataLayout &DL,
+                                  ASTContext &Context) {
+  if (!DL.isLegalInteger(Info.Size))
+    return false;
+  // Make sure Field is natually aligned.
+  if (Info.Offset %
+          (DL.getABIIntegerTypeAlignment(Info.Size) * Context.getCharWidth()) !=
+      0)
+    return false;
+  return true;
+}
+
 RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV,
                                                  SourceLocation Loc) {
   const CGBitFieldInfo &Info = LV.getBitFieldInfo();
 
   // Get the output type.
   llvm::Type *ResLTy = ConvertType(LV.getType());
 
   Address Ptr = LV.getBitFieldAddress();
-  llvm::Value *Val = Builder.CreateLoad(Ptr, LV.isVolatileQualified(), "bf.load");
+  llvm::Value *Val = nullptr;
+  if (CGM.getCodeGenOpts().SplitBitfields &&
+      IsSeparatableBitfield(Info, CGM.getDataLayout(), getContext())) {
+    // Ptr is the pointer to the Bitfield Storage. Add Offset to the Ptr
+    // if the Offset is not zero.
+    if (Info.Offset != 0) {
+      Address BC = Builder.CreateBitCast(Ptr, Int8PtrTy);
+      llvm::Value *GEP = Builder.CreateGEP(
+          BC.getPointer(),
+          llvm::ConstantInt::get(Int32Ty,
+                                 Info.Offset / getContext().getCharWidth()));
+      Ptr = Address(GEP, Ptr.getAlignment());
+    }
+    llvm::Type *BFType = llvm::Type::getIntNTy(getLLVMContext(), Info.Size);
+    CharUnits Align = getContext().toCharUnitsFromBits(
+        CGM.getDataLayout().getABITypeAlignment(BFType) *
+        getContext().getCharWidth());
+    if (Ptr.getElementType()->getScalarSizeInBits() != Info.Size) {
+      // Adjust the element type of Ptr if it has different size with Info.Size.
+      llvm::Type *BFTypePtr =
+          llvm::Type::getIntNPtrTy(getLLVMContext(), Info.Size);
+      Ptr = Builder.CreateBitCast(Address(Ptr.getPointer(), Align), BFTypePtr);
+    } else if (Ptr.getAlignment() != Align) {
+      // Adjust the alignment of Ptr.
+      Ptr = Address(Ptr.getPointer(), Align);
+    }
 
-  if (Info.IsSigned) {
-    assert(static_cast<unsigned>(Info.Offset + Info.Size) <= Info.StorageSize);
-    unsigned HighBits = Info.StorageSize - Info.Offset - Info.Size;
-    if (HighBits)
-      Val = Builder.CreateShl(Val, HighBits, "bf.shl");
-    if (Info.Offset + HighBits)
-      Val = Builder.CreateAShr(Val, Info.Offset + HighBits, "bf.ashr");
+    Val = Builder.CreateLoad(Ptr, LV.isVolatileQualified(), "bf.load");
   } else {
-    if (Info.Offset)
-      Val = Builder.CreateLShr(Val, Info.Offset, "bf.lshr");
-    if (static_cast<unsigned>(Info.Offset) + Info.Size < Info.StorageSize)
-      Val = Builder.CreateAnd(Val, llvm::APInt::getLowBitsSet(Info.StorageSize,
-                                                              Info.Size),
-                              "bf.clear");
+    Val = Builder.CreateLoad(Ptr, LV.isVolatileQualified(), "bf.load");
+    if (Info.IsSigned) {
+      assert(static_cast<unsigned>(Info.Offset + Info.Size) <=
+             Info.StorageSize);
+      unsigned HighBits = Info.StorageSize - Info.Offset - Info.Size;
+      if (HighBits)
+        Val = Builder.CreateShl(Val, HighBits, "bf.shl");
+      if (Info.Offset + HighBits)
+        Val = Builder.CreateAShr(Val, Info.Offset + HighBits, "bf.ashr");
+    } else {
+      if (Info.Offset)
+        Val = Builder.CreateLShr(Val, Info.Offset, "bf.lshr");
+      if (static_cast<unsigned>(Info.Offset) + Info.Size < Info.StorageSize)
+        Val = Builder.CreateAnd(
+            Val, llvm::APInt::getLowBitsSet(Info.StorageSize, Info.Size),
+            "bf.clear");
+    }
   }
   Val = Builder.CreateIntCast(Val, ResLTy, Info.IsSigned, "bf.cast");
   EmitScalarRangeCheck(Val, LV.getType(), Loc);
@@ -1887,15 +1933,46 @@
 
   // Get the source value, truncated to the width of the bit-field.
   llvm::Value *SrcVal = Src.getScalarVal();
+  llvm::Value *MaskedVal;
 
-  // Cast the source to the storage type and shift it into place.
-  SrcVal = Builder.CreateIntCast(SrcVal, Ptr.getElementType(),
-                                 /*IsSigned=*/false);
-  llvm::Value *MaskedVal = SrcVal;
-
-  // See if there are other bits in the bitfield's storage we'll need to load
-  // and mask together with source before storing.
-  if (Info.StorageSize != Info.Size) {
+  bool SeparatableBitfield =
+      CGM.getCodeGenOpts().SplitBitfields &&
+      IsSeparatableBitfield(Info, CGM.getDataLayout(), getContext());
+  if (SeparatableBitfield) {
+    // Ptr is the pointer to the Bitfield Storage. Add Offset to the Ptr
+    // if the Offset is not zero.
+    if (Info.Offset != 0) {
+      Address BC = Builder.CreateBitCast(Ptr, Int8PtrTy);
+      llvm::Value *GEP = Builder.CreateGEP(
+          BC.getPointer(),
+          llvm::ConstantInt::get(Int32Ty,
+                                 Info.Offset / getContext().getCharWidth()));
+      Ptr = Address(GEP, Ptr.getAlignment());
+    }
+    llvm::Type *BFType = llvm::Type::getIntNTy(getLLVMContext(), Info.Size);
+    CharUnits Align = getContext().toCharUnitsFromBits(
+        CGM.getDataLayout().getABITypeAlignment(BFType) *
+        getContext().getCharWidth());
+    if (Ptr.getElementType()->getScalarSizeInBits() != Info.Size) {
+      // Adjust the element type of Ptr if it has different size with Info.Size.
+      llvm::Type *BFTypePtr =
+          llvm::Type::getIntNPtrTy(getLLVMContext(), Info.Size);
+      Ptr = Builder.CreateBitCast(Address(Ptr.getPointer(), Align), BFTypePtr);
+    } else if (Ptr.getAlignment() != Align) {
+      // Adjust the alignment of Ptr.
+      Ptr = Address(Ptr.getPointer(), Align);
+    }
+    // Cast the source to the storage type and shift it into place.
+    SrcVal = Builder.CreateIntCast(SrcVal, Ptr.getElementType(),
+                                   /*IsSigned=*/false);
+    MaskedVal = SrcVal;
+  } else if (Info.StorageSize != Info.Size) {
+    // Cast the source to the storage type and shift it into place.
+    SrcVal = Builder.CreateIntCast(SrcVal, Ptr.getElementType(),
+                                   /*IsSigned=*/false);
+
+    // See if there are other bits in the bitfield's storage we'll need to load
+    // and mask together with source before storing.
     assert(Info.StorageSize > Info.Size && "Invalid bitfield size.");
     llvm::Value *Val =
       Builder.CreateLoad(Ptr, Dst.isVolatileQualified(), "bf.load");
@@ -1921,6 +1998,10 @@
     SrcVal = Builder.CreateOr(Val, SrcVal, "bf.set");
   } else {
     assert(Info.Offset == 0);
+    // Cast the source to the storage type and shift it into place.
+    SrcVal = Builder.CreateIntCast(SrcVal, Ptr.getElementType(),
+                                   /*IsSigned=*/false);
+    MaskedVal = SrcVal;
   }
 
   // Write the new value back out.
@@ -1931,7 +2012,7 @@
     llvm::Value *ResultVal = MaskedVal;
 
     // Sign extend the value if needed.
-    if (Info.IsSigned) {
+    if (Info.IsSigned && !SeparatableBitfield) {
       assert(Info.Size <= Info.StorageSize);
       unsigned HighBits = Info.StorageSize - Info.Size;
       if (HighBits) {
Index: include/clang/Frontend/CodeGenOptions.def
===================================================================
--- include/clang/Frontend/CodeGenOptions.def
+++ include/clang/Frontend/CodeGenOptions.def
@@ -177,6 +177,8 @@
 CODEGENOPT(SanitizeStats     , 1, 0) ///< Collect statistics for sanitizers.
 CODEGENOPT(SimplifyLibCalls  , 1, 1) ///< Set when -fbuiltin is enabled.
 CODEGENOPT(SoftFloat         , 1, 0) ///< -soft-float.
+CODEGENOPT(SplitBitfields     , 1, 0) ///< Split bitfield with proper width and
+                                      ///< alignment.
 CODEGENOPT(StrictEnums       , 1, 0) ///< Optimize based on strict enum definition.
 CODEGENOPT(StrictVTablePointers, 1, 0) ///< Optimize based on the strict vtable pointers
 CODEGENOPT(TimePasses        , 1, 0) ///< Set when -ftime-report is enabled.
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -1029,6 +1029,13 @@
   Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Filename defining the whitelist for imbuing the 'never instrument' XRay attribute.">;
 
+def fsplit_bitfields : Flag<["-"], "fsplit-bitfields">,
+  Group<f_clang_Group>, Flags<[CC1Option]>,
+  HelpText<"Enable splitting bitfield with legal width and alignment in LLVM.">;
+def fno_split_bitfields : Flag<["-"], "fno-split-bitfields">,
+  Group<f_clang_Group>, Flags<[CC1Option]>,
+  HelpText<"Disable splitting bitfield with legal width and alignment in LLVM.">;
+
 def flat__namespace : Flag<["-"], "flat_namespace">;
 def flax_vector_conversions : Flag<["-"], "flax-vector-conversions">, Group<f_Group>;
 def flimited_precision_EQ : Joined<["-"], "flimited-precision=">, Group<f_Group>;
Index: include/clang/Basic/DiagnosticDriverKinds.td
===================================================================
--- include/clang/Basic/DiagnosticDriverKinds.td
+++ include/clang/Basic/DiagnosticDriverKinds.td
@@ -326,4 +326,8 @@
   "unable to find a Visual Studio installation; "
   "try running Clang from a developer command prompt">,
   InGroup<DiagGroup<"msvc-not-found">>;
+
+def warn_drv_split_bitfields_ignored : Warning<
+  "option '-fsplit-bitfields' cannot be enabled together with sanitizer; flag ignored">,
+  InGroup<OptionIgnored>;
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to