Skip to content

Commit

Permalink
[clang][Interp] Basic support for bit fields
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D155270
  • Loading branch information
tbaederr committed Aug 10, 2023
1 parent ab4e4a6 commit 8065b1c
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 13 deletions.
15 changes: 11 additions & 4 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,10 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
return Discard(this->emitDiv(*T, BO));
case BO_Assign:
if (DiscardResult)
return this->emitStorePop(*T, BO);
return this->emitStore(*T, BO);
return LHS->refersToBitField() ? this->emitStoreBitFieldPop(*T, BO)
: this->emitStorePop(*T, BO);
return LHS->refersToBitField() ? this->emitStoreBitField(*T, BO)
: this->emitStore(*T, BO);
case BO_And:
return Discard(this->emitBitAnd(*T, BO));
case BO_Or:
Expand Down Expand Up @@ -1590,8 +1592,13 @@ bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) {
if (!this->visit(Init))
return false;

if (!this->emitInitField(*T, FieldToInit->Offset, Initializer))
return false;
if (FieldToInit->isBitField()) {
if (!this->emitInitBitField(*T, FieldToInit, Initializer))
return false;
} else {
if (!this->emitInitField(*T, FieldToInit->Offset, Initializer))
return false;
}

if (!this->emitPopPtr(Initializer))
return false;
Expand Down
9 changes: 7 additions & 2 deletions clang/lib/AST/Interp/ByteCodeStmtGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,13 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
if (!this->visit(InitExpr))
return false;

if (!this->emitInitThisField(*T, F->Offset, InitExpr))
return false;
if (F->isBitField()) {
if (!this->emitInitThisBitField(*T, F, InitExpr))
return false;
} else {
if (!this->emitInitThisField(*T, F->Offset, InitExpr))
return false;
}
} else {
// Non-primitive case. Get a pointer to the field-to-initialize
// on the stack and call visitInitialzer() for it.
Expand Down
14 changes: 7 additions & 7 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,7 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
assert(F->isBitField());
if (S.checkingPotentialConstantExpression())
return false;
const Pointer &This = S.Current->getThis();
Expand Down Expand Up @@ -1048,8 +1049,9 @@ bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {

template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
assert(F->isBitField());
const T &Value = S.Stk.pop<T>();
const Pointer &Field = S.Stk.pop<Pointer>().atField(F->Offset);
const Pointer &Field = S.Stk.peek<Pointer>().atField(F->Offset);
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
Field.activate();
Field.initialize();
Expand Down Expand Up @@ -1247,11 +1249,10 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) {
return false;
if (!Ptr.isRoot())
Ptr.initialize();
if (auto *FD = Ptr.getField()) {
if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
} else {
else
Ptr.deref<T>() = Value;
}
return true;
}

Expand All @@ -1263,11 +1264,10 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
return false;
if (!Ptr.isRoot())
Ptr.initialize();
if (auto *FD = Ptr.getField()) {
if (const auto *FD = Ptr.getField())
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
} else {
else
Ptr.deref<T>() = Value;
}
return true;
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Record.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class Record final {
const FieldDecl *Decl;
unsigned Offset;
Descriptor *Desc;
bool isBitField() const { return Decl->isBitField(); }
};

/// Describes a base class.
Expand Down
47 changes: 47 additions & 0 deletions clang/test/AST/Interp/bitfields.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -Wno-bitfield-constant-conversion -verify %s
// RUN: %clang_cc1 -verify=ref -Wno-bitfield-constant-conversion %s

// expected-no-diagnostics
// ref-no-diagnostics

namespace Basic {
struct A {
unsigned int a : 2;
constexpr A() : a(0) {}
constexpr A(int a) : a(a) {}
};

constexpr A a{1};
static_assert(a.a == 1, "");

constexpr A a2{10};
static_assert(a2.a == 2, "");


constexpr int storeA() {
A a;
a.a = 10;

return a.a;
}
static_assert(storeA() == 2, "");

constexpr int storeA2() {
A a;
return a.a = 10;
}
static_assert(storeA2() == 2, "");

// TODO: +=, -=, etc. operators.
}

namespace Overflow {
struct A {int c:3;};

constexpr int f() {
A a1{3};
return a1.c++;
}

static_assert(f() == 3, "");
}

0 comments on commit 8065b1c

Please sign in to comment.