diff options
Diffstat (limited to 'clang/lib/AST/ByteCode')
| -rw-r--r-- | clang/lib/AST/ByteCode/Compiler.cpp | 190 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Compiler.h | 4 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Descriptor.cpp | 2 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Descriptor.h | 4 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Disasm.cpp | 1 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Interp.cpp | 22 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Interp.h | 209 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/InterpBlock.h | 5 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/InterpBuiltin.cpp | 3 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/InterpFrame.cpp | 5 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Opcodes.td | 28 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Pointer.cpp | 11 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/Pointer.h | 5 | ||||
| -rw-r--r-- | clang/lib/AST/ByteCode/State.h | 1 |
14 files changed, 364 insertions, 126 deletions
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index afa3b7ea7de7..ea473730350b 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -25,6 +25,34 @@ using APSInt = llvm::APSInt; namespace clang { namespace interp { +static bool refersToUnion(const Expr *E) { + for (;;) { + if (const auto *ME = dyn_cast<MemberExpr>(E)) { + if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl()); + FD && FD->getParent()->isUnion()) + return true; + E = ME->getBase(); + continue; + } + + if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) { + E = ASE->getBase()->IgnoreImplicit(); + continue; + } + + if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E); + ICE && (ICE->getCastKind() == CK_NoOp || + ICE->getCastKind() == CK_DerivedToBase || + ICE->getCastKind() == CK_UncheckedDerivedToBase)) { + E = ICE->getSubExpr(); + continue; + } + + break; + } + return false; +} + static std::optional<bool> getBoolValue(const Expr *E) { if (const auto *CE = dyn_cast_if_present<ConstantExpr>(E); CE && CE->hasAPValueResult() && @@ -880,22 +908,11 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { return this->VisitPointerArithBinOp(BO); } - // Assignments require us to evalute the RHS first. - if (BO->getOpcode() == BO_Assign) { - - if (!visit(RHS) || !visit(LHS)) - return false; - - // We don't support assignments in C. - if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(BO)) - return false; + if (BO->getOpcode() == BO_Assign) + return this->visitAssignment(LHS, RHS, BO); - if (!this->emitFlip(*LT, *RT, BO)) - return false; - } else { - if (!visit(LHS) || !visit(RHS)) - return false; - } + if (!visit(LHS) || !visit(RHS)) + return false; // For languages such as C, cast the result of one // of our comparision opcodes to T (which is usually int). @@ -946,22 +963,6 @@ bool Compiler<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { if (BO->getType()->isFloatingType()) return Discard(this->emitDivf(getFPOptions(BO), BO)); return Discard(this->emitDiv(*T, BO)); - case BO_Assign: - if (DiscardResult) - return LHS->refersToBitField() ? this->emitStoreBitFieldPop(*T, BO) - : this->emitStorePop(*T, BO); - if (LHS->refersToBitField()) { - if (!this->emitStoreBitField(*T, BO)) - return false; - } else { - if (!this->emitStore(*T, BO)) - return false; - } - // Assignments aren't necessarily lvalues in C. - // Load from them in that case. - if (!BO->isLValue()) - return this->emitLoadPop(*T, BO); - return true; case BO_And: return Discard(this->emitBitAnd(*T, BO)); case BO_Or: @@ -1790,19 +1791,26 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits, return this->delegate(Inits[0]); auto initPrimitiveField = [=](const Record::Field *FieldToInit, - const Expr *Init, PrimType T) -> bool { + const Expr *Init, PrimType T, + bool Activate = false) -> bool { InitStackScope<Emitter> ISS(this, isa<CXXDefaultInitExpr>(Init)); InitLinkScope<Emitter> ILS(this, InitLink::Field(FieldToInit->Offset)); if (!this->visit(Init)) return false; - if (FieldToInit->isBitField()) + bool BitField = FieldToInit->isBitField(); + if (BitField && Activate) + return this->emitInitBitFieldActivate(T, FieldToInit, E); + if (BitField) return this->emitInitBitField(T, FieldToInit, E); + if (Activate) + return this->emitInitFieldActivate(T, FieldToInit->Offset, E); return this->emitInitField(T, FieldToInit->Offset, E); }; auto initCompositeField = [=](const Record::Field *FieldToInit, - const Expr *Init) -> bool { + const Expr *Init, + bool Activate = false) -> bool { InitStackScope<Emitter> ISS(this, isa<CXXDefaultInitExpr>(Init)); InitLinkScope<Emitter> ILS(this, InitLink::Field(FieldToInit->Offset)); @@ -1810,6 +1818,10 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits, // on the stack and recurse into visitInitializer(). if (!this->emitGetPtrField(FieldToInit->Offset, Init)) return false; + + if (Activate && !this->emitActivate(E)) + return false; + if (!this->visitInitializer(Init)) return false; return this->emitPopPtr(E); @@ -1829,10 +1841,10 @@ bool Compiler<Emitter>::visitInitList(ArrayRef<const Expr *> Inits, const Record::Field *FieldToInit = R->getField(FToInit); if (std::optional<PrimType> T = classify(Init)) { - if (!initPrimitiveField(FieldToInit, Init, *T)) + if (!initPrimitiveField(FieldToInit, Init, *T, /*Activate=*/true)) return false; } else { - if (!initCompositeField(FieldToInit, Init)) + if (!initCompositeField(FieldToInit, Init, /*Activate=*/true)) return false; } } @@ -2023,7 +2035,8 @@ bool Compiler<Emitter>::visitArrayElemInit(unsigned ElemIndex, const Expr *Init, template <class Emitter> bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args, - const FunctionDecl *FuncDecl) { + const FunctionDecl *FuncDecl, + bool Activate) { assert(VarScope->getKind() == ScopeKind::Call); llvm::BitVector NonNullArgs = collectNonNullArgs(FuncDecl, Args); @@ -2046,6 +2059,11 @@ bool Compiler<Emitter>::visitCallArgs(ArrayRef<const Expr *> Args, return false; } + if (ArgIndex == 1 && Activate) { + if (!this->emitActivate(Arg)) + return false; + } + if (FuncDecl && NonNullArgs[ArgIndex]) { PrimType ArgT = classify(Arg).value_or(PT_Ptr); if (ArgT == PT_Ptr) { @@ -4227,10 +4245,13 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R, PrimType T = classifyPrim(D->getType()); if (!this->visitZeroInitializer(T, QT, E)) return false; + if (R->isUnion()) { + if (!this->emitInitFieldActivate(T, Field.Offset, E)) + return false; + break; + } if (!this->emitInitField(T, Field.Offset, E)) return false; - if (R->isUnion()) - break; continue; } @@ -4256,13 +4277,15 @@ bool Compiler<Emitter>::visitZeroRecordInitializer(const Record *R, } else return false; - if (!this->emitFinishInitPop(E)) - return false; - // C++11 [dcl.init]p5: If T is a (possibly cv-qualified) union type, the // object's first non-static named data member is zero-initialized - if (R->isUnion()) + if (R->isUnion()) { + if (!this->emitFinishInitActivatePop(E)) + return false; break; + } + if (!this->emitFinishInitPop(E)) + return false; } for (const Record::Base &B : R->bases()) { @@ -4326,6 +4349,59 @@ bool Compiler<Emitter>::visitZeroArrayInitializer(QualType T, const Expr *E) { } template <class Emitter> +bool Compiler<Emitter>::visitAssignment(const Expr *LHS, const Expr *RHS, + const Expr *E) { + if (!classify(E->getType())) + return false; + + if (!this->visit(RHS)) + return false; + if (!this->visit(LHS)) + return false; + + // We don't support assignments in C. + if (!Ctx.getLangOpts().CPlusPlus && !this->emitInvalid(E)) + return false; + + PrimType RHT = classifyPrim(RHS); + bool Activates = refersToUnion(LHS); + bool BitField = LHS->refersToBitField(); + + if (!this->emitFlip(PT_Ptr, RHT, E)) + return false; + + if (DiscardResult) { + if (BitField && Activates) + return this->emitStoreBitFieldActivatePop(RHT, E); + if (BitField) + return this->emitStoreBitFieldPop(RHT, E); + if (Activates) + return this->emitStoreActivatePop(RHT, E); + // Otherwise, regular non-activating store. + return this->emitStorePop(RHT, E); + } + + auto maybeLoad = [&](bool Result) -> bool { + if (!Result) + return false; + // Assignments aren't necessarily lvalues in C. + // Load from them in that case. + if (!E->isLValue()) + return this->emitLoadPop(RHT, E); + return true; + }; + + if (BitField && Activates) + return maybeLoad(this->emitStoreBitFieldActivate(RHT, E)); + if (BitField) + return maybeLoad(this->emitStoreBitField(RHT, E)); + if (Activates) + return maybeLoad(this->emitStoreActivate(RHT, E)); + // Otherwise, regular non-activating store. + return maybeLoad(this->emitStore(RHT, E)); +} + +template <class Emitter> template <typename T> bool Compiler<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) { switch (Ty) { @@ -5067,7 +5143,7 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) { return false; } - if (!this->visitCallArgs(Args, FuncDecl)) + if (!this->visitCallArgs(Args, FuncDecl, IsAssignmentOperatorCall)) return false; // Undo the argument reversal we did earlier. @@ -5851,7 +5927,8 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { assert(!ReturnType); auto emitFieldInitializer = [&](const Record::Field *F, unsigned FieldOffset, - const Expr *InitExpr) -> bool { + const Expr *InitExpr, + bool Activate = false) -> bool { // We don't know what to do with these, so just return false. if (InitExpr->getType().isNull()) return false; @@ -5860,8 +5937,13 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { if (!this->visit(InitExpr)) return false; - if (F->isBitField()) + bool BitField = F->isBitField(); + if (BitField && Activate) + return this->emitInitThisBitFieldActivate(*T, F, FieldOffset, InitExpr); + if (BitField) return this->emitInitThisBitField(*T, F, FieldOffset, InitExpr); + if (Activate) + return this->emitInitThisFieldActivate(*T, FieldOffset, InitExpr); return this->emitInitThisField(*T, FieldOffset, InitExpr); } // Non-primitive case. Get a pointer to the field-to-initialize @@ -5870,6 +5952,9 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { if (!this->emitGetPtrThisField(FieldOffset, InitExpr)) return false; + if (Activate && !this->emitActivate(InitExpr)) + return false; + if (!this->visitInitializer(InitExpr)) return false; @@ -5880,8 +5965,9 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { const Record *R = this->getRecord(RD); if (!R) return false; + bool IsUnion = R->isUnion(); - if (R->isUnion() && Ctor->isCopyOrMoveConstructor()) { + if (IsUnion && Ctor->isCopyOrMoveConstructor()) { if (R->getNumFields() == 0) return this->emitRetVoid(Ctor); // union copy and move ctors are special. @@ -5908,7 +5994,7 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { if (const FieldDecl *Member = Init->getMember()) { const Record::Field *F = R->getField(Member); - if (!emitFieldInitializer(F, F->Offset, InitExpr)) + if (!emitFieldInitializer(F, F->Offset, InitExpr, IsUnion)) return false; } else if (const Type *Base = Init->getBaseClass()) { const auto *BaseDecl = Base->getAsCXXRecordDecl(); @@ -5928,11 +6014,15 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { return false; } + if (IsUnion && !this->emitActivate(InitExpr)) + return false; + if (!this->visitInitializer(InitExpr)) return false; if (!this->emitFinishInitPop(InitExpr)) return false; } else if (const IndirectFieldDecl *IFD = Init->getIndirectMember()) { + assert(IFD->getChainingSize() >= 2); unsigned NestedFieldOffset = 0; @@ -5944,12 +6034,14 @@ bool Compiler<Emitter>::compileConstructor(const CXXConstructorDecl *Ctor) { NestedField = FieldRecord->getField(FD); assert(NestedField); + IsUnion = IsUnion || FieldRecord->isUnion(); NestedFieldOffset += NestedField->Offset; } assert(NestedField); - if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr)) + if (!emitFieldInitializer(NestedField, NestedFieldOffset, InitExpr, + IsUnion)) return false; // Mark all chain links as initialized. diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h index 05ba7460b343..debee6726853 100644 --- a/clang/lib/AST/ByteCode/Compiler.h +++ b/clang/lib/AST/ByteCode/Compiler.h @@ -307,7 +307,8 @@ protected: const Expr *E); bool visitArrayElemInit(unsigned ElemIndex, const Expr *Init, std::optional<PrimType> InitT); - bool visitCallArgs(ArrayRef<const Expr *> Args, const FunctionDecl *FuncDecl); + bool visitCallArgs(ArrayRef<const Expr *> Args, const FunctionDecl *FuncDecl, + bool Activate); /// Creates a local primitive value. unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsConst, @@ -342,6 +343,7 @@ private: bool visitZeroInitializer(PrimType T, QualType QT, const Expr *E); bool visitZeroRecordInitializer(const Record *R, const Expr *E); bool visitZeroArrayInitializer(QualType T, const Expr *E); + bool visitAssignment(const Expr *LHS, const Expr *RHS, const Expr *E); /// Emits an APSInt constant. bool emitConst(const llvm::APSInt &Value, PrimType Ty, const Expr *E); diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp b/clang/lib/AST/ByteCode/Descriptor.cpp index c89eca9bef44..5b9f44518fcc 100644 --- a/clang/lib/AST/ByteCode/Descriptor.cpp +++ b/clang/lib/AST/ByteCode/Descriptor.cpp @@ -162,6 +162,8 @@ static void initField(Block *B, std::byte *Ptr, bool IsConst, bool IsMutable, Desc->IsConst = IsConst || D->IsConst; Desc->IsFieldMutable = IsMutable || D->IsMutable; Desc->IsVolatile = IsVolatile || D->IsVolatile; + // True if this field is const AND the parent is mutable. + Desc->IsConstInMutable = Desc->IsConst && IsMutable; if (auto Fn = D->CtorFn) Fn(B, Ptr + FieldOffset, Desc->IsConst, Desc->IsFieldMutable, diff --git a/clang/lib/AST/ByteCode/Descriptor.h b/clang/lib/AST/ByteCode/Descriptor.h index 4591eabb69bb..0227e4c0c7e3 100644 --- a/clang/lib/AST/ByteCode/Descriptor.h +++ b/clang/lib/AST/ByteCode/Descriptor.h @@ -101,6 +101,10 @@ struct InlineDescriptor { /// Flag indicating if the field is mutable (if in a record). LLVM_PREFERRED_TYPE(bool) unsigned IsFieldMutable : 1; + /// Flag indicating if this field is a const field nested in + /// a mutable parent field. + LLVM_PREFERRED_TYPE(bool) + unsigned IsConstInMutable : 1; /// Flag indicating if the field is an element of a composite array. LLVM_PREFERRED_TYPE(bool) unsigned IsArrayElement : 1; diff --git a/clang/lib/AST/ByteCode/Disasm.cpp b/clang/lib/AST/ByteCode/Disasm.cpp index f64501f4a31e..74399d177b5a 100644 --- a/clang/lib/AST/ByteCode/Disasm.cpp +++ b/clang/lib/AST/ByteCode/Disasm.cpp @@ -445,6 +445,7 @@ LLVM_DUMP_METHOD void InlineDescriptor::dump(llvm::raw_ostream &OS) const { OS << "InUnion: " << InUnion << "\n"; OS << "IsFieldMutable: " << IsFieldMutable << "\n"; OS << "IsArrayElement: " << IsArrayElement << "\n"; + OS << "IsConstInMutable: " << IsConstInMutable << '\n'; OS << "Desc: "; if (Desc) Desc->dump(OS); diff --git a/clang/lib/AST/ByteCode/Interp.cpp b/clang/lib/AST/ByteCode/Interp.cpp index 457de2bed37d..df5e3be83d74 100644 --- a/clang/lib/AST/ByteCode/Interp.cpp +++ b/clang/lib/AST/ByteCode/Interp.cpp @@ -326,7 +326,6 @@ bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return true; assert(Ptr.inUnion()); - assert(Ptr.isField() && Ptr.getField()); Pointer U = Ptr.getBase(); Pointer C = Ptr; @@ -567,7 +566,10 @@ bool CheckDowncast(InterpState &S, CodePtr OpPC, const Pointer &Ptr, bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { assert(Ptr.isLive() && "Pointer is not live"); - if (!Ptr.isConst() || Ptr.isMutable()) + if (!Ptr.isConst()) + return true; + + if (Ptr.isMutable() && !Ptr.isConstInMutable()) return true; if (!Ptr.isBlockPointer()) @@ -575,7 +577,7 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { // The This pointer is writable in constructors and destructors, // even if isConst() returns true. - if (llvm::find(S.InitializingBlocks, Ptr.block())) + if (llvm::is_contained(S.InitializingBlocks, Ptr.block())) return true; const QualType Ty = Ptr.getType(); @@ -805,6 +807,8 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return false; if (!CheckRange(S, OpPC, Ptr, AK_Assign)) return false; + if (!CheckActive(S, OpPC, Ptr, AK_Assign)) + return false; if (!CheckGlobal(S, OpPC, Ptr)) return false; if (!CheckConst(S, OpPC, Ptr)) @@ -814,7 +818,7 @@ bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { return true; } -bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { +static bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr) { if (!CheckLive(S, OpPC, Ptr, AK_MemberCall)) return false; if (!Ptr.isDummy()) { @@ -936,7 +940,7 @@ bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) { return false; } -bool CheckCallDepth(InterpState &S, CodePtr OpPC) { +static bool CheckCallDepth(InterpState &S, CodePtr OpPC) { if ((S.Current->getDepth() + 1) > S.getLangOpts().ConstexprCallDepth) { S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_depth_limit_exceeded) @@ -1091,8 +1095,8 @@ bool CheckDummy(InterpState &S, CodePtr OpPC, const Pointer &Ptr, return false; } -bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F, - const CallExpr *CE, unsigned ArgSize) { +static bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F, + const CallExpr *CE, unsigned ArgSize) { auto Args = ArrayRef(CE->getArgs(), CE->getNumArgs()); auto NonNullArgs = collectNonNullArgs(F->getDecl(), Args); unsigned Offset = 0; @@ -1500,7 +1504,6 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func, if (!CheckInvoke(S, OpPC, ThisPtr)) return cleanup(); if (!Func->isConstructor() && !Func->isDestructor() && - !Func->isCopyOrMoveOperator() && !CheckActive(S, OpPC, ThisPtr, AK_MemberCall)) return false; } @@ -1773,6 +1776,9 @@ bool CheckNewTypeMismatch(InterpState &S, CodePtr OpPC, const Expr *E, std::optional<uint64_t> ArraySize) { const Pointer &Ptr = S.Stk.peek<Pointer>(); + if (Ptr.inUnion() && Ptr.getBase().getRecord()->isUnion()) + Ptr.activate(); + // Similar to CheckStore(), but with the additional CheckTemporary() call and // the AccessKinds are different. if (!CheckTemporary(S, OpPC, Ptr, AK_Construct)) diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index 9d17f96c97c8..ce0ebdd8321b 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -98,26 +98,12 @@ bool CheckGlobalInitialized(InterpState &S, CodePtr OpPC, const Pointer &Ptr); /// Checks if a value can be stored in a block. bool CheckStore(InterpState &S, CodePtr OpPC, const Pointer &Ptr); -/// Checks if a method can be invoked on an object. -bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr); - /// Checks if a value can be initialized. bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr); -/// Checks if a method can be called. -bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F); - -/// Checks if calling the currently active function would exceed -/// the allowed call depth. -bool CheckCallDepth(InterpState &S, CodePtr OpPC); - /// Checks the 'this' pointer. bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This); -/// Checks if all the arguments annotated as 'nonnull' are in fact not null. -bool CheckNonNullArgs(InterpState &S, CodePtr OpPC, const Function *F, - const CallExpr *CE, unsigned ArgSize); - /// Checks if dynamic memory allocation is available in the current /// language mode. bool CheckDynamicMemoryAllocation(InterpState &S, CodePtr OpPC); @@ -1592,6 +1578,21 @@ bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) { if (!CheckThis(S, OpPC, This)) return false; const Pointer &Field = This.atField(I); + assert(Field.canBeInitialized()); + Field.deref<T>() = S.Stk.pop<T>(); + Field.initialize(); + return true; +} + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool InitThisFieldActivate(InterpState &S, CodePtr OpPC, uint32_t I) { + if (S.checkingPotentialConstantExpression() && S.Current->getDepth() == 0) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + const Pointer &Field = This.atField(I); + assert(Field.canBeInitialized()); Field.deref<T>() = S.Stk.pop<T>(); Field.activate(); Field.initialize(); @@ -1610,12 +1611,31 @@ bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F, if (!CheckThis(S, OpPC, This)) return false; const Pointer &Field = This.atField(FieldOffset); + assert(Field.canBeInitialized()); const auto &Value = S.Stk.pop<T>(); Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue()); Field.initialize(); return true; } +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool InitThisBitFieldActivate(InterpState &S, CodePtr OpPC, + const Record::Field *F, uint32_t FieldOffset) { + assert(F->isBitField()); + if (S.checkingPotentialConstantExpression() && S.Current->getDepth() == 0) + return false; + const Pointer &This = S.Current->getThis(); + if (!CheckThis(S, OpPC, This)) + return false; + const Pointer &Field = This.atField(FieldOffset); + assert(Field.canBeInitialized()); + const auto &Value = S.Stk.pop<T>(); + Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue()); + Field.initialize(); + Field.activate(); + return true; +} + /// 1) Pops the value from the stack /// 2) Peeks a pointer from the stack /// 3) Pushes the value to field I of the pointer on the stack @@ -1627,6 +1647,18 @@ bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) { return false; const Pointer &Field = Ptr.atField(I); Field.deref<T>() = Value; + Field.initialize(); + return true; +} + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool InitFieldActivate(InterpState &S, CodePtr OpPC, uint32_t I) { + const T &Value = S.Stk.pop<T>(); + const Pointer &Ptr = S.Stk.peek<Pointer>(); + if (!CheckRange(S, OpPC, Ptr, CSK_Field)) + return false; + const Pointer &Field = Ptr.atField(I); + Field.deref<T>() = Value; Field.activate(); Field.initialize(); return true; @@ -1653,6 +1685,32 @@ bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) { } else { Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue()); } + Field.initialize(); + return true; +} + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool InitBitFieldActivate(InterpState &S, CodePtr OpPC, + const Record::Field *F) { + assert(F->isBitField()); + const T &Value = S.Stk.pop<T>(); + const Pointer &Field = S.Stk.peek<Pointer>().atField(F->Offset); + + if constexpr (needsAlloc<T>()) { + T Result = S.allocAP<T>(Value.bitWidth()); + if (T::isSigned()) + Result.copy(Value.toAPSInt() + .trunc(F->Decl->getBitWidthValue()) + .sextOrTrunc(Value.bitWidth())); + else + Result.copy(Value.toAPSInt() + .trunc(F->Decl->getBitWidthValue()) + .zextOrTrunc(Value.bitWidth())); + + Field.deref<T>() = Result; + } else { + Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue()); + } Field.activate(); Field.initialize(); return true; @@ -1695,32 +1753,6 @@ inline bool GetPtrThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { return true; } -inline bool GetPtrActiveField(InterpState &S, CodePtr OpPC, uint32_t Off) { - const Pointer &Ptr = S.Stk.pop<Pointer>(); - if (!CheckNull(S, OpPC, Ptr, CSK_Field)) - return false; - if (!CheckRange(S, OpPC, Ptr, CSK_Field)) - return false; - Pointer Field = Ptr.atField(Off); - Ptr.deactivate(); - Field.activate(); - S.Stk.push<Pointer>(std::move(Field)); - return true; -} - -inline bool GetPtrActiveThisField(InterpState &S, CodePtr OpPC, uint32_t Off) { - if (S.checkingPotentialConstantExpression()) - return false; - const Pointer &This = S.Current->getThis(); - if (!CheckThis(S, OpPC, This)) - return false; - Pointer Field = This.atField(Off); - This.deactivate(); - Field.activate(); - S.Stk.push<Pointer>(std::move(Field)); - return true; -} - inline bool GetPtrDerivedPop(InterpState &S, CodePtr OpPC, uint32_t Off, bool NullOK, const Type *TargetType) { const Pointer &Ptr = S.Stk.pop<Pointer>(); @@ -1816,6 +1848,20 @@ inline bool GetPtrThisBase(InterpState &S, CodePtr OpPC, uint32_t Off) { inline bool FinishInitPop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); + if (Ptr.canBeInitialized()) + Ptr.initialize(); + return true; +} + +inline bool FinishInit(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.peek<Pointer>(); + if (Ptr.canBeInitialized()) + Ptr.initialize(); + return true; +} + +inline bool FinishInitActivate(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.peek<Pointer>(); if (Ptr.canBeInitialized()) { Ptr.initialize(); Ptr.activate(); @@ -1823,8 +1869,8 @@ inline bool FinishInitPop(InterpState &S, CodePtr OpPC) { return true; } -inline bool FinishInit(InterpState &S, CodePtr OpPC) { - const Pointer &Ptr = S.Stk.peek<Pointer>(); +inline bool FinishInitActivatePop(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.pop<Pointer>(); if (Ptr.canBeInitialized()) { Ptr.initialize(); Ptr.activate(); @@ -1902,10 +1948,8 @@ bool Store(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; - if (Ptr.canBeInitialized()) { + if (Ptr.canBeInitialized()) Ptr.initialize(); - Ptr.activate(); - } Ptr.deref<T>() = Value; return true; } @@ -1916,10 +1960,46 @@ bool StorePop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (Ptr.canBeInitialized()) + Ptr.initialize(); + Ptr.deref<T>() = Value; + return true; +} + +static inline bool Activate(InterpState &S, CodePtr OpPC) { + const Pointer &Ptr = S.Stk.peek<Pointer>(); + if (Ptr.canBeInitialized()) + Ptr.activate(); + return true; +} + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool StoreActivate(InterpState &S, CodePtr OpPC) { + const T &Value = S.Stk.pop<T>(); + const Pointer &Ptr = S.Stk.peek<Pointer>(); + + if (Ptr.canBeInitialized()) { + Ptr.initialize(); + Ptr.activate(); + } + + if (!CheckStore(S, OpPC, Ptr)) + return false; + Ptr.deref<T>() = Value; + return true; +} + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool StoreActivatePop(InterpState &S, CodePtr OpPC) { + const T &Value = S.Stk.pop<T>(); + const Pointer &Ptr = S.Stk.pop<Pointer>(); + if (Ptr.canBeInitialized()) { Ptr.initialize(); Ptr.activate(); } + if (!CheckStore(S, OpPC, Ptr)) + return false; Ptr.deref<T>() = Value; return true; } @@ -1930,10 +2010,8 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; - if (Ptr.canBeInitialized()) { + if (Ptr.canBeInitialized()) Ptr.initialize(); - Ptr.activate(); - } if (const auto *FD = Ptr.getField()) Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue()); else @@ -1947,10 +2025,43 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckStore(S, OpPC, Ptr)) return false; + if (Ptr.canBeInitialized()) + Ptr.initialize(); + if (const auto *FD = Ptr.getField()) + Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue()); + else + Ptr.deref<T>() = Value; + return true; +} + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool StoreBitFieldActivate(InterpState &S, CodePtr OpPC) { + const T &Value = S.Stk.pop<T>(); + const Pointer &Ptr = S.Stk.peek<Pointer>(); + if (Ptr.canBeInitialized()) { + Ptr.initialize(); + Ptr.activate(); + } + if (!CheckStore(S, OpPC, Ptr)) + return false; + if (const auto *FD = Ptr.getField()) + Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue()); + else + Ptr.deref<T>() = Value; + return true; +} + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool StoreBitFieldActivatePop(InterpState &S, CodePtr OpPC) { + const T &Value = S.Stk.pop<T>(); + const Pointer &Ptr = S.Stk.pop<Pointer>(); + if (Ptr.canBeInitialized()) { Ptr.initialize(); Ptr.activate(); } + if (!CheckStore(S, OpPC, Ptr)) + return false; if (const auto *FD = Ptr.getField()) Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue()); else @@ -1964,7 +2075,6 @@ bool Init(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.peek<Pointer>(); if (!CheckInit(S, OpPC, Ptr)) return false; - Ptr.activate(); Ptr.initialize(); new (&Ptr.deref<T>()) T(Value); return true; @@ -1976,7 +2086,6 @@ bool InitPop(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); if (!CheckInit(S, OpPC, Ptr)) return false; - Ptr.activate(); Ptr.initialize(); new (&Ptr.deref<T>()) T(Value); return true; diff --git a/clang/lib/AST/ByteCode/InterpBlock.h b/clang/lib/AST/ByteCode/InterpBlock.h index 7798b6f886a8..51622238e275 100644 --- a/clang/lib/AST/ByteCode/InterpBlock.h +++ b/clang/lib/AST/ByteCode/InterpBlock.h @@ -14,11 +14,6 @@ #define LLVM_CLANG_AST_INTERP_BLOCK_H #include "Descriptor.h" -#include "clang/AST/ComparisonCategories.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/Expr.h" -#include "llvm/ADT/PointerUnion.h" #include "llvm/Support/raw_ostream.h" namespace clang { diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index de0b97fd93c7..462b9a11e0a5 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "../ExprConstShared.h" #include "Boolean.h" -#include "Compiler.h" #include "EvalEmitter.h" #include "Interp.h" #include "InterpBuiltinBitCast.h" @@ -2906,6 +2905,8 @@ static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src, if (!copyField(F, /*Activate=*/true)) return false; } else { + if (!CheckMutable(S, OpPC, Src.atField(F.Offset))) + return false; Pointer DestField = Dest.atField(F.Offset); zeroAll(DestField); } diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp b/clang/lib/AST/ByteCode/InterpFrame.cpp index a5a4bd25fe71..d62a4f6275b5 100644 --- a/clang/lib/AST/ByteCode/InterpFrame.cpp +++ b/clang/lib/AST/ByteCode/InterpFrame.cpp @@ -128,6 +128,11 @@ static bool shouldSkipInBacktrace(const Function *F) { if (FD->getDeclName().getCXXOverloadedOperator() == OO_New || FD->getDeclName().getCXXOverloadedOperator() == OO_Array_New) return true; + + if (const auto *MD = dyn_cast<CXXMethodDecl>(FD); + MD && MD->getParent()->isAnonymousStructOrUnion()) + return true; + return false; } diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td index 57e01f7bd9da..804853d29512 100644 --- a/clang/lib/AST/ByteCode/Opcodes.td +++ b/clang/lib/AST/ByteCode/Opcodes.td @@ -315,10 +315,6 @@ def GetPtrGlobal : OffsetOpcode; // [Pointer] -> [Pointer] def GetPtrField : OffsetOpcode; def GetPtrFieldPop : OffsetOpcode; -// [Pointer] -> [Pointer] -def GetPtrActiveField : OffsetOpcode; -// [] -> [Pointer] -def GetPtrActiveThisField : OffsetOpcode; // [] -> [Pointer] def GetPtrThisField : OffsetOpcode; // [Pointer] -> [Pointer] @@ -330,9 +326,10 @@ def GetMemberPtrBasePop : Opcode { let Args = [ArgSint32]; } - def FinishInitPop : Opcode; -def FinishInit : Opcode; +def FinishInit : Opcode; +def FinishInitActivate : Opcode; +def FinishInitActivatePop : Opcode; def FinishInitGlobal : Opcode; def GetPtrDerivedPop : Opcode { let Args = [ArgUint32, ArgBool, ArgTypePtr]; } @@ -460,16 +457,24 @@ def SetThisField : AccessOpcode; // [Value] -> [] def InitThisField : AccessOpcode; +def InitThisFieldActivate : AccessOpcode; // [Value] -> [] def InitThisBitField : Opcode { let Types = [AluTypeClass]; let Args = [ArgRecordField, ArgUint32]; let HasGroup = 1; } +def InitThisBitFieldActivate : Opcode { + let Types = [AluTypeClass]; + let Args = [ArgRecordField, ArgUint32]; + let HasGroup = 1; +} // [Pointer, Value] -> [] def InitField : AccessOpcode; +def InitFieldActivate : AccessOpcode; // [Pointer, Value] -> [] def InitBitField : BitFieldOpcode; +def InitBitFieldActivate : BitFieldOpcode; //===----------------------------------------------------------------------===// // Pointer access @@ -495,15 +500,16 @@ class StoreBitFieldOpcode : Opcode { let HasGroup = 1; } -// [Pointer, Value] -> [Pointer] def Store : StoreOpcode {} -// [Pointer, Value] -> [] def StorePop : StoreOpcode {} - -// [Pointer, Value] -> [Pointer] +def StoreActivatePop : StoreOpcode {} +def StoreActivate : StoreOpcode {} def StoreBitField : StoreBitFieldOpcode {} -// [Pointer, Value] -> [] def StoreBitFieldPop : StoreBitFieldOpcode {} +def StoreBitFieldActivate : StoreBitFieldOpcode {} +def StoreBitFieldActivatePop : StoreBitFieldOpcode {} + +def Activate : Opcode {} // [Pointer, Value] -> [] def Init : StoreOpcode {} diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp index 159b4238a6a0..2f9ecf98e558 100644 --- a/clang/lib/AST/ByteCode/Pointer.cpp +++ b/clang/lib/AST/ByteCode/Pointer.cpp @@ -502,8 +502,17 @@ void Pointer::activate() const { if (!getInlineDesc()->InUnion) return; - auto activate = [](Pointer &P) -> void { + std::function<void(Pointer &)> activate; + activate = [&activate](Pointer &P) -> void { P.getInlineDesc()->IsActive = true; + if (const Record *R = P.getRecord(); R && !R->isUnion()) { + for (const Record::Field &F : R->fields()) { + Pointer FieldPtr = P.atField(F.Offset); + if (!FieldPtr.getInlineDesc()->IsActive) + activate(FieldPtr); + } + // FIXME: Bases? + } }; std::function<void(Pointer &)> deactivate; diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index e6a64e6658f0..da74013cf83a 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -576,6 +576,11 @@ public: return true; return isRoot() ? getDeclDesc()->IsConst : getInlineDesc()->IsConst; } + bool isConstInMutable() const { + if (!isBlockPointer()) + return false; + return isRoot() ? false : getInlineDesc()->IsConstInMutable; + } /// Checks if an object or a subfield is volatile. bool isVolatile() const { diff --git a/clang/lib/AST/ByteCode/State.h b/clang/lib/AST/ByteCode/State.h index 9a81fa6b7d22..6fc33222ac95 100644 --- a/clang/lib/AST/ByteCode/State.h +++ b/clang/lib/AST/ByteCode/State.h @@ -35,6 +35,7 @@ enum AccessKinds { AK_Construct, AK_Destroy, AK_IsWithinLifetime, + AK_Dereference }; /// The order of this enum is important for diagnostics. |
