[cfe-commits] r54395 - in /cfe/trunk: lib/CodeGen/CGExpr.cpp test/CodeGen/2008-07-22-packed-bitfield-access.c

Daniel Dunbar daniel at zuster.org
Tue Aug 5 22:08:46 PDT 2008


Author: ddunbar
Date: Wed Aug  6 00:08:45 2008
New Revision: 54395

URL: http://llvm.org/viewvc/llvm-project?rev=54395&view=rev
Log:
Fix bitfield accesses which straddle the boundary of the underlying
type.

 - This generates somewhat less optimal code than before but this is
   not hard to rectify once stable (at the cost of slightly more
   complex code).

 - This currently always uses little-endian ordering of the bitfield. 

 - This breaks the CodeGen/bitfield.c test because it was grepping for
   hard-coded assembly instructions. Will fix once a better test case
   is constructed (hard to do without execution).

 - This fixes SingleSource/UnitTests/2006-01-23-InitializedBitField.c
   and Regression/C/PR1386.c from the test suite.

 - <rdar://problem/6085090>, <rdar://problem/6094169>

Added:
    cfe/trunk/test/CodeGen/2008-07-22-packed-bitfield-access.c
Modified:
    cfe/trunk/lib/CodeGen/CGExpr.cpp

Modified: cfe/trunk/lib/CodeGen/CGExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExpr.cpp?rev=54395&r1=54394&r2=54395&view=diff

==============================================================================
--- cfe/trunk/lib/CodeGen/CGExpr.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExpr.cpp Wed Aug  6 00:08:45 2008
@@ -173,28 +173,65 @@
 
 RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV,
                                                  QualType ExprType) {
+  unsigned StartBit = LV.getBitfieldStartBit();
+  unsigned BitfieldSize = LV.getBitfieldSize();
   llvm::Value *Ptr = LV.getBitfieldAddr();
-  const llvm::Type *EltTy =
+
+  const llvm::Type *EltTy = 
     cast<llvm::PointerType>(Ptr->getType())->getElementType();
-  unsigned EltTySize = EltTy->getPrimitiveSizeInBits();
-  unsigned short BitfieldSize = LV.getBitfieldSize();
-  unsigned short EndBit = LV.getBitfieldStartBit() + BitfieldSize;
-
-  llvm::Value *V = Builder.CreateLoad(Ptr, LV.isVolatileQualified(), "tmp");
-
-  llvm::Value *ShAmt = llvm::ConstantInt::get(EltTy, EltTySize - EndBit);
-  V = Builder.CreateShl(V, ShAmt, "tmp");
-
-  ShAmt = llvm::ConstantInt::get(EltTy, EltTySize - BitfieldSize);
-  V = LV.isBitfieldSigned() ?
-    Builder.CreateAShr(V, ShAmt, "tmp") :
-    Builder.CreateLShr(V, ShAmt, "tmp");
+  unsigned EltTySize = CGM.getTargetData().getTypeSizeInBits(EltTy);
+
+  // In some cases the bitfield may straddle two memory locations.
+  // Currently we load the entire bitfield, then do the magic to
+  // sign-extend it if necessary. This results in somewhat more code
+  // than necessary for the common case (one load), since two shifts
+  // accomplish both the masking and sign extension.
+  unsigned LowBits = std::min(BitfieldSize, EltTySize - StartBit);
+  llvm::Value *Val = Builder.CreateLoad(Ptr, LV.isVolatileQualified(), "tmp");
+  
+  // Shift to proper location.
+  Val = Builder.CreateLShr(Val, llvm::ConstantInt::get(EltTy, StartBit), 
+                           "bf.lo");
+  
+  // Mask off unused bits.
+  llvm::Constant *LowMask = 
+    llvm::ConstantInt::get(llvm::APInt::getLowBitsSet(EltTySize, LowBits));
+  Val = Builder.CreateAnd(Val, LowMask, "bf.lo.cleared");
+  
+  // Fetch the high bits if necessary.
+  if (LowBits < BitfieldSize) {
+    unsigned HighBits = BitfieldSize - LowBits;
+    llvm::Value *HighPtr = 
+      Builder.CreateGEP(Ptr, llvm::ConstantInt::get(llvm::Type::Int32Ty, 1),
+                        "bf.ptr.hi");    
+    llvm::Value *HighVal = Builder.CreateLoad(HighPtr, 
+                                              LV.isVolatileQualified(),
+                                              "tmp");
+    
+    // Mask off unused bits.
+    llvm::Constant *HighMask = 
+      llvm::ConstantInt::get(llvm::APInt::getLowBitsSet(EltTySize, HighBits));
+    HighVal = Builder.CreateAnd(HighVal, HighMask, "bf.lo.cleared");
+
+    // Shift to proper location and or in to bitfield value.
+    HighVal = Builder.CreateShl(HighVal, 
+                                llvm::ConstantInt::get(EltTy, LowBits));
+    Val = Builder.CreateOr(Val, HighVal, "bf.val");
+  }
+
+  // Sign extend if necessary.
+  if (LV.isBitfieldSigned()) {
+    llvm::Value *ExtraBits = llvm::ConstantInt::get(EltTy, 
+                                                    EltTySize - BitfieldSize);
+    Val = Builder.CreateAShr(Builder.CreateShl(Val, ExtraBits), 
+                             ExtraBits, "bf.val.sext");
+  }
 
   // The bitfield type and the normal type differ when the storage sizes
   // differ (currently just _Bool).
-  V = Builder.CreateIntCast(V, ConvertType(ExprType), false, "tmp");
+  Val = Builder.CreateIntCast(Val, ConvertType(ExprType), false, "tmp");
 
-  return RValue::get(V);
+  return RValue::get(Val);
 }
 
 // If this is a reference to a subset of the elements of a vector, either
@@ -297,40 +334,73 @@
 
 void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst,
                                                      QualType Ty) {
-  unsigned short StartBit = Dst.getBitfieldStartBit();
-  unsigned short BitfieldSize = Dst.getBitfieldSize();
+  unsigned StartBit = Dst.getBitfieldStartBit();
+  unsigned BitfieldSize = Dst.getBitfieldSize();
   llvm::Value *Ptr = Dst.getBitfieldAddr();
 
-  llvm::Value *NewVal = Src.getScalarVal();
-  llvm::Value *OldVal = Builder.CreateLoad(Ptr, Dst.isVolatileQualified(),
-                                           "tmp");
-
-  // The bitfield type and the normal type differ when the storage sizes
-  // differ (currently just _Bool).
-  const llvm::Type *EltTy = OldVal->getType();
-  unsigned EltTySize = CGM.getTargetData().getABITypeSizeInBits(EltTy);
-
-  NewVal = Builder.CreateIntCast(NewVal, EltTy, false, "tmp");
-
-  // Move the bits into the appropriate location
-  llvm::Value *ShAmt = llvm::ConstantInt::get(EltTy, StartBit);
-  NewVal = Builder.CreateShl(NewVal, ShAmt, "tmp");
-
-  llvm::Constant *Mask = llvm::ConstantInt::get(
-           llvm::APInt::getBitsSet(EltTySize, StartBit,
-                                   StartBit + BitfieldSize));
-
-  // Mask out any bits that shouldn't be set in the result.
-  NewVal = Builder.CreateAnd(NewVal, Mask, "tmp");
-
-  // Next, mask out the bits this bit-field should include from the old value.
-  Mask = llvm::ConstantExpr::getNot(Mask);
-  OldVal = Builder.CreateAnd(OldVal, Mask, "tmp");
+  const llvm::Type *EltTy = 
+    cast<llvm::PointerType>(Ptr->getType())->getElementType();
+  unsigned EltTySize = CGM.getTargetData().getTypeSizeInBits(EltTy);
 
-  // Finally, merge the two together and store it.
-  NewVal = Builder.CreateOr(OldVal, NewVal, "tmp");
+  // Get the new value, cast to the appropriate type and masked to
+  // exactly the size of the bit-field.
+  llvm::Value *NewVal = Src.getScalarVal();
+  NewVal = Builder.CreateIntCast(NewVal, EltTy, false, "tmp");  
+  llvm::Constant *Mask = 
+    llvm::ConstantInt::get(llvm::APInt::getLowBitsSet(EltTySize, BitfieldSize));
+  NewVal = Builder.CreateAnd(NewVal, Mask, "bf.value");
+
+  // In some cases the bitfield may straddle two memory locations.
+  // Emit the low part first and check to see if the high needs to be
+  // done.
+  unsigned LowBits = std::min(BitfieldSize, EltTySize - StartBit);
+  llvm::Value *LowVal = Builder.CreateLoad(Ptr, Dst.isVolatileQualified(),
+                                           "bf.prev.low");
+
+  // Compute the mask for zero-ing the low part of this bitfield.
+  llvm::Constant *InvMask = 
+    llvm::ConstantInt::get(~llvm::APInt::getBitsSet(EltTySize, StartBit, 
+                                                    StartBit + LowBits));
+  
+  // Compute the new low part as
+  //   LowVal = (LowVal & InvMask) | (NewVal << StartBit),
+  // with the shift of NewVal implicitly stripping the high bits.
+  llvm::Value *NewLowVal = 
+    Builder.CreateShl(NewVal, llvm::ConstantInt::get(EltTy, StartBit), 
+                      "bf.value.lo");  
+  LowVal = Builder.CreateAnd(LowVal, InvMask, "bf.prev.lo.cleared");
+  LowVal = Builder.CreateOr(LowVal, NewLowVal, "bf.new.lo");
+    
+  // Write back.
+  Builder.CreateStore(LowVal, Ptr, Dst.isVolatileQualified());
 
-  Builder.CreateStore(NewVal, Ptr, Dst.isVolatileQualified());
+  // If the low part doesn't cover the bitfield emit a high part.
+  if (LowBits < BitfieldSize) {
+    unsigned HighBits = BitfieldSize - LowBits;
+    llvm::Value *HighPtr = 
+      Builder.CreateGEP(Ptr, llvm::ConstantInt::get(llvm::Type::Int32Ty, 1),
+                        "bf.ptr.hi");    
+    llvm::Value *HighVal = Builder.CreateLoad(HighPtr, 
+                                              Dst.isVolatileQualified(),
+                                              "bf.prev.hi");
+    
+    // Compute the mask for zero-ing the high part of this bitfield.
+    llvm::Constant *InvMask = 
+      llvm::ConstantInt::get(~llvm::APInt::getLowBitsSet(EltTySize, HighBits));
+  
+    // Compute the new high part as
+    //   HighVal = (HighVal & InvMask) | (NewVal lshr LowBits),
+    // where the high bits of NewVal have already been cleared and the
+    // shift stripping the low bits.
+    llvm::Value *NewHighVal = 
+      Builder.CreateLShr(NewVal, llvm::ConstantInt::get(EltTy, LowBits), 
+                        "bf.value.high");  
+    HighVal = Builder.CreateAnd(HighVal, InvMask, "bf.prev.hi.cleared");
+    HighVal = Builder.CreateOr(HighVal, NewHighVal, "bf.new.hi");
+    
+    // Write back.
+    Builder.CreateStore(HighVal, HighPtr, Dst.isVolatileQualified());
+  }
 }
 
 void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src,

Added: cfe/trunk/test/CodeGen/2008-07-22-packed-bitfield-access.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/2008-07-22-packed-bitfield-access.c?rev=54395&view=auto

==============================================================================
--- cfe/trunk/test/CodeGen/2008-07-22-packed-bitfield-access.c (added)
+++ cfe/trunk/test/CodeGen/2008-07-22-packed-bitfield-access.c Wed Aug  6 00:08:45 2008
@@ -0,0 +1,10 @@
+// RUN: clang %s -emit-llvm -o -
+
+int main () {
+  struct foo {
+    unsigned a:16;
+    unsigned b:32 __attribute__ ((packed));
+  } x;
+  x.b = 0x56789abcL;
+  return 0;
+}





More information about the cfe-commits mailing list