diff options
author | Daniel Dunbar <daniel@zuster.org> | 2008-08-06 05:08:45 +0000 |
---|---|---|
committer | Daniel Dunbar <daniel@zuster.org> | 2008-08-06 05:08:45 +0000 |
commit | 10e3dedb6f6bf999e23700f876f7e19e650ef642 (patch) | |
tree | 3ebf1c376b75781e96752d54840639fdf3ff0866 /lib/CodeGen/CGExpr.cpp | |
parent | 1fc01fa64d4992d87c37f8a75632530ed686dec4 (diff) |
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>
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@54395 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/CodeGen/CGExpr.cpp')
-rw-r--r-- | lib/CodeGen/CGExpr.cpp | 162 |
1 files changed, 116 insertions, 46 deletions
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index ca52b070dd..7c5feb169b 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -173,28 +173,65 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, QualType ExprType) { RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV, QualType ExprType) { + unsigned StartBit = LV.getBitfieldStartBit(); + unsigned BitfieldSize = LV.getBitfieldSize(); llvm::Value *Ptr = LV.getBitfieldAddr(); - 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"); + const llvm::Type *EltTy = + cast<llvm::PointerType>(Ptr->getType())->getElementType(); + 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"); + } - ShAmt = llvm::ConstantInt::get(EltTy, EltTySize - BitfieldSize); - V = LV.isBitfieldSigned() ? - Builder.CreateAShr(V, ShAmt, "tmp") : - Builder.CreateLShr(V, ShAmt, "tmp"); + // 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::EmitStoreThroughLValue(RValue Src, LValue Dst, 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"); - - // Finally, merge the two together and store it. - NewVal = Builder.CreateOr(OldVal, NewVal, "tmp"); + const llvm::Type *EltTy = + cast<llvm::PointerType>(Ptr->getType())->getElementType(); + unsigned EltTySize = CGM.getTargetData().getTypeSizeInBits(EltTy); - Builder.CreateStore(NewVal, Ptr, Dst.isVolatileQualified()); + // 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()); + + // 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, |