Skip to content

Commit 6a24339

Browse files
author
Diogo Sampaio
committed
[ARM] Follow AACPS standard for volatile bit-fields access width
Summary: This patch resumes the work of D16586. According to the AAPCS, volatile bit-fields should be accessed using containers of the widht of their declarative type. In such case: ``` struct S1 { short a : 1; } ``` should be accessed using load and stores of the width (sizeof(short)), where now the compiler does only load the minimum required width (char in this case). However, as discussed in D16586, that could overwrite non-volatile bit-fields, which conflicted with C and C++ object models by creating data race conditions that are not part of the bit-field, e.g. ``` struct S2 { short a; int b : 16; } ``` Accessing `S2.b` would also access `S2.a`. The AAPCS Release 2019Q1.1 (https://static.docs.arm.com/ihi0042/g/aapcs32.pdf) section 8.1 Data Types, page 35, "Volatile bit-fields - preserving number and width of container accesses" has been updated to avoid conflict with the C++ Memory Model. Now it reads in the note: ``` This ABI does not place any restrictions on the access widths of bit-fields where the container overlaps with a non-bit-field member. This is because the C/C++ memory model defines these as being separate memory locations, which can be accessed by two threads simultaneously. For this reason, compilers must be permitted to use a narrower memory access width (including splitting the access into multiple instructions) to avoid writing to a different memory location. ``` I've updated the patch D16586 to follow such behavior by verifying that we only change volatile bit-field access when: - it won't overlap with any other non-bit-field member - we only access memory inside the bounds of the record Regarding the number of memory accesses, that should be preserved, that will be implemented by D67399. Reviewers: rsmith, rjmccall, eli.friedman, ostannard Subscribers: ostannard, kristof.beyls, cfe-commits, carwil, olista01 Tags: #clang Differential Revision: https://reviews.llvm.org/D72932
1 parent f04284c commit 6a24339

File tree

4 files changed

+350
-134
lines changed

4 files changed

+350
-134
lines changed

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ llvm::Value *CodeGenFunction::EvaluateExprAsBool(const Expr *E) {
177177
Loc);
178178
}
179179

180+
// Helper method to check if the underlying ABI is AAPCS
181+
static bool isAAPCS(const TargetInfo &TargetInfo) {
182+
return TargetInfo.getABI().startswith("aapcs");
183+
}
184+
180185
/// EmitIgnoredExpr - Emit code to compute the specified expression,
181186
/// ignoring the result.
182187
void CodeGenFunction::EmitIgnoredExpr(const Expr *E) {
@@ -4052,15 +4057,120 @@ static bool hasAnyVptr(const QualType Type, const ASTContext &Context) {
40524057
return false;
40534058
}
40544059

4060+
// AAPCS requires volatile bitfield accesses to be performed using the
4061+
// natural alignment / width of the bitfield declarative type, if that
4062+
// won't cause overlap over a non-bitfield member nor access outside the
4063+
// the data structure.
4064+
bool CodeGenFunction::AdjustAAPCSBitfieldLValue(Address &Base,
4065+
CGBitFieldInfo &Info,
4066+
const FieldDecl *Field,
4067+
const QualType FieldType,
4068+
const CGRecordLayout &RL) {
4069+
llvm::Type *ResLTy = ConvertTypeForMem(FieldType);
4070+
// CGRecordLowering::setBitFieldInfo() pre-adjusts the bitfield offsets for
4071+
// big-endian targets, but it assumes a container of width Info.StorageSize.
4072+
// Since AAPCS uses a different container size (width of the type), we first
4073+
// undo that calculation here and redo it once the bitfield offset within the
4074+
// new container is calculated
4075+
const bool BE = CGM.getTypes().getDataLayout().isBigEndian();
4076+
const unsigned OldOffset =
4077+
BE ? Info.StorageSize - (Info.Offset + Info.Size) : Info.Offset;
4078+
// Offset to the bitfield from the beginning of the struct
4079+
const unsigned AbsoluteOffset =
4080+
getContext().toBits(Info.StorageOffset) + OldOffset;
4081+
4082+
// Container size is the width of the bitfield type
4083+
const unsigned ContainerSize = ResLTy->getPrimitiveSizeInBits();
4084+
// Nothing to do if the access uses the desired
4085+
// container width and is naturally aligned
4086+
if (Info.StorageSize == ContainerSize && (OldOffset % ContainerSize == 0))
4087+
return false;
4088+
4089+
// Offset within the container
4090+
unsigned MemberOffset = AbsoluteOffset & (ContainerSize - 1);
4091+
4092+
// Bail out if an aligned load of the container cannot cover the entire
4093+
// bitfield. This can happen for example, if the bitfield is part of a packed
4094+
// struct. AAPCS does not define access rules for such cases, we let clang to
4095+
// follow its own rules.
4096+
if (MemberOffset + Info.Size > ContainerSize) {
4097+
return false;
4098+
}
4099+
// Re-adjust offsets for big-endian targets
4100+
if (BE)
4101+
MemberOffset = ContainerSize - (MemberOffset + Info.Size);
4102+
4103+
const CharUnits NewOffset =
4104+
getContext().toCharUnitsFromBits(AbsoluteOffset & ~(ContainerSize - 1));
4105+
const CharUnits End = NewOffset +
4106+
getContext().toCharUnitsFromBits(ContainerSize) -
4107+
CharUnits::One();
4108+
4109+
const ASTRecordLayout &Layout =
4110+
getContext().getASTRecordLayout(Field->getParent());
4111+
// If we access outside memory outside the record, than bail out
4112+
const CharUnits RecordSize = Layout.getSize();
4113+
if (End >= RecordSize) {
4114+
return false;
4115+
}
4116+
4117+
// Bail out if performing this load would access non-bitfields members
4118+
4119+
for (auto it : Field->getParent()->fields()) {
4120+
const FieldDecl &F = *it;
4121+
// We distinct allow bitfields overlaps
4122+
if (F.isBitField())
4123+
continue;
4124+
const CharUnits FOffset = getContext().toCharUnitsFromBits(
4125+
Layout.getFieldOffset(F.getFieldIndex()));
4126+
const CharUnits FEnd =
4127+
FOffset +
4128+
getContext().toCharUnitsFromBits(
4129+
ConvertTypeForMem(F.getType())->getPrimitiveSizeInBits()) -
4130+
CharUnits::One();
4131+
if (End < FOffset) {
4132+
// The other field starts after the desired load end.
4133+
break;
4134+
}
4135+
if (FEnd < NewOffset) {
4136+
// The other field ends before the desired load offset.
4137+
continue;
4138+
}
4139+
// The desired load overlaps a non-bitfiel member, bail out.
4140+
return false;
4141+
}
4142+
4143+
// Write the new bitfield access parameters
4144+
Info.StorageOffset = NewOffset;
4145+
Info.StorageSize = ContainerSize;
4146+
Info.Offset = MemberOffset;
4147+
// GEP into the bitfield container. Here we essentially treat the Base as a
4148+
// pointer to a block of containers and index into it appropriately
4149+
Base =
4150+
Builder.CreateConstInBoundsGEP(Builder.CreateElementBitCast(Base, ResLTy),
4151+
AbsoluteOffset / ContainerSize);
4152+
return true;
4153+
}
4154+
40554155
LValue CodeGenFunction::EmitLValueForField(LValue base,
40564156
const FieldDecl *field) {
40574157
LValueBaseInfo BaseInfo = base.getBaseInfo();
40584158

40594159
if (field->isBitField()) {
40604160
const CGRecordLayout &RL =
4061-
CGM.getTypes().getCGRecordLayout(field->getParent());
4062-
const CGBitFieldInfo &Info = RL.getBitFieldInfo(field);
4161+
CGM.getTypes().getCGRecordLayout(field->getParent());
4162+
CGBitFieldInfo Info = RL.getBitFieldInfo(field);
40634163
Address Addr = base.getAddress(*this);
4164+
const QualType FieldType =
4165+
field->getType().withCVRQualifiers(base.getVRQualifiers());
4166+
4167+
if (isAAPCS(CGM.getTarget()) && FieldType.isVolatileQualified()) {
4168+
if (AdjustAAPCSBitfieldLValue(Addr, Info, field, FieldType, RL)) {
4169+
return LValue::MakeBitfield(Addr, Info, FieldType, BaseInfo,
4170+
TBAAAccessInfo());
4171+
}
4172+
}
4173+
40644174
unsigned Idx = RL.getLLVMFieldNo(field);
40654175
const RecordDecl *rec = field->getParent();
40664176
if (!IsInPreservedAIRegion &&
@@ -4082,11 +4192,9 @@ LValue CodeGenFunction::EmitLValueForField(LValue base,
40824192
if (Addr.getElementType() != FieldIntTy)
40834193
Addr = Builder.CreateElementBitCast(Addr, FieldIntTy);
40844194

4085-
QualType fieldType =
4086-
field->getType().withCVRQualifiers(base.getVRQualifiers());
40874195
// TODO: Support TBAA for bit fields.
40884196
LValueBaseInfo FieldBaseInfo(BaseInfo.getAlignmentSource());
4089-
return LValue::MakeBitfield(Addr, Info, fieldType, FieldBaseInfo,
4197+
return LValue::MakeBitfield(Addr, Info, FieldType, FieldBaseInfo,
40904198
TBAAAccessInfo());
40914199
}
40924200

clang/lib/CodeGen/CGValue.h

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,13 @@
1414
#ifndef LLVM_CLANG_LIB_CODEGEN_CGVALUE_H
1515
#define LLVM_CLANG_LIB_CODEGEN_CGVALUE_H
1616

17+
#include "Address.h"
18+
#include "CGRecordLayout.h"
19+
#include "CodeGenTBAA.h"
1720
#include "clang/AST/ASTContext.h"
1821
#include "clang/AST/Type.h"
19-
#include "llvm/IR/Value.h"
2022
#include "llvm/IR/Type.h"
21-
#include "Address.h"
22-
#include "CodeGenTBAA.h"
23+
#include "llvm/IR/Value.h"
2324

2425
namespace llvm {
2526
class Constant;
@@ -181,11 +182,11 @@ class LValue {
181182

182183
// ExtVector element subset: V.xyx
183184
llvm::Constant *VectorElts;
184-
185-
// BitField start bit and size
186-
const CGBitFieldInfo *BitFieldInfo;
187185
};
188186

187+
// BitField start bit and size
188+
CGBitFieldInfo BitFieldInfo;
189+
189190
QualType Type;
190191

191192
// 'const' is unused here
@@ -357,10 +358,13 @@ class LValue {
357358
Address getBitFieldAddress() const {
358359
return Address(getBitFieldPointer(), getAlignment());
359360
}
360-
llvm::Value *getBitFieldPointer() const { assert(isBitField()); return V; }
361+
llvm::Value *getBitFieldPointer() const {
362+
assert(isBitField());
363+
return V;
364+
}
361365
const CGBitFieldInfo &getBitFieldInfo() const {
362366
assert(isBitField());
363-
return *BitFieldInfo;
367+
return BitFieldInfo;
364368
}
365369

366370
// global register lvalue
@@ -415,7 +419,7 @@ class LValue {
415419
LValue R;
416420
R.LVType = BitField;
417421
R.V = Addr.getPointer();
418-
R.BitFieldInfo = &Info;
422+
R.BitFieldInfo = Info;
419423
R.Initialize(type, type.getQualifiers(), Addr.getAlignment(), BaseInfo,
420424
TBAAInfo);
421425
return R;

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1619,8 +1619,14 @@ class CodeGenFunction : public CodeGenTypeCache {
16191619
void EmitOpenCLKernelMetadata(const FunctionDecl *FD,
16201620
llvm::Function *Fn);
16211621

1622+
/// Perform AAPCS specific tweaks on volatile bitfield accesses.
1623+
bool AdjustAAPCSBitfieldLValue(Address &Base, CGBitFieldInfo &Info,
1624+
const FieldDecl *Field,
1625+
const QualType FieldType,
1626+
const CGRecordLayout &RL);
1627+
16221628
public:
1623-
CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext=false);
1629+
CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext = false);
16241630
~CodeGenFunction();
16251631

16261632
CodeGenTypes &getTypes() const { return CGM.getTypes(); }

0 commit comments

Comments
 (0)