https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/105641
>From 7c9321e85febf0fa177736f425fe286d24951eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbae...@redhat.com> Date: Wed, 21 Aug 2024 09:49:54 +0200 Subject: [PATCH] [clang][Interp] Allow adding offsets to function pointers Convert them to Pointers, do the offset calculation and then convert them back to function pointers. --- clang/lib/AST/ByteCode/Compiler.cpp | 40 ++++++++++++++++---- clang/lib/AST/ByteCode/FunctionPointer.cpp | 43 ++++++++++++++++++++++ clang/lib/AST/ByteCode/FunctionPointer.h | 41 +++++---------------- clang/lib/AST/ByteCode/Interp.h | 37 ++++++++++++++++--- clang/lib/AST/ByteCode/Pointer.h | 6 ++- clang/lib/AST/CMakeLists.txt | 1 + clang/test/AST/ByteCode/c.c | 16 ++++++++ 7 files changed, 139 insertions(+), 45 deletions(-) create mode 100644 clang/lib/AST/ByteCode/FunctionPointer.cpp diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp index 10f3222726fd43..d16d2840cc0e48 100644 --- a/clang/lib/AST/ByteCode/Compiler.cpp +++ b/clang/lib/AST/ByteCode/Compiler.cpp @@ -885,12 +885,21 @@ bool Compiler<Emitter>::VisitPointerArithBinOp(const BinaryOperator *E) { if (!LT || !RT) return false; + // Visit the given pointer expression and optionally convert to a PT_Ptr. + auto visitAsPointer = [&](const Expr *E, PrimType T) -> bool { + if (!this->visit(E)) + return false; + if (T != PT_Ptr) + return this->emitDecayPtr(T, PT_Ptr, E); + return true; + }; + if (LHS->getType()->isPointerType() && RHS->getType()->isPointerType()) { if (Op != BO_Sub) return false; assert(E->getType()->isIntegerType()); - if (!visit(RHS) || !visit(LHS)) + if (!visitAsPointer(RHS, *RT) || !visitAsPointer(LHS, *LT)) return false; return this->emitSubPtr(classifyPrim(E->getType()), E); @@ -898,21 +907,38 @@ bool Compiler<Emitter>::VisitPointerArithBinOp(const BinaryOperator *E) { PrimType OffsetType; if (LHS->getType()->isIntegerType()) { - if (!visit(RHS) || !visit(LHS)) + if (!visitAsPointer(RHS, *RT)) + return false; + if (!this->visit(LHS)) return false; OffsetType = *LT; } else if (RHS->getType()->isIntegerType()) { - if (!visit(LHS) || !visit(RHS)) + if (!visitAsPointer(LHS, *LT)) + return false; + if (!this->visit(RHS)) return false; OffsetType = *RT; } else { return false; } - if (Op == BO_Add) - return this->emitAddOffset(OffsetType, E); - else if (Op == BO_Sub) - return this->emitSubOffset(OffsetType, E); + // Do the operation and optionally transform to + // result pointer type. + if (Op == BO_Add) { + if (!this->emitAddOffset(OffsetType, E)) + return false; + + if (classifyPrim(E) != PT_Ptr) + return this->emitDecayPtr(PT_Ptr, classifyPrim(E), E); + return true; + } else if (Op == BO_Sub) { + if (!this->emitSubOffset(OffsetType, E)) + return false; + + if (classifyPrim(E) != PT_Ptr) + return this->emitDecayPtr(PT_Ptr, classifyPrim(E), E); + return true; + } return false; } diff --git a/clang/lib/AST/ByteCode/FunctionPointer.cpp b/clang/lib/AST/ByteCode/FunctionPointer.cpp new file mode 100644 index 00000000000000..6b0b559a63386e --- /dev/null +++ b/clang/lib/AST/ByteCode/FunctionPointer.cpp @@ -0,0 +1,43 @@ +//===----------------------- FunctionPointer.cpp ----------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "FunctionPointer.h" + +namespace clang { +namespace interp { + +APValue FunctionPointer::toAPValue(const ASTContext &) const { + if (!Func) + return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {}, + /*OnePastTheEnd=*/false, /*IsNull=*/true); + + if (!Valid) + return APValue(static_cast<Expr *>(nullptr), + CharUnits::fromQuantity(getIntegerRepresentation()), {}, + /*OnePastTheEnd=*/false, /*IsNull=*/false); + + if (Func->getDecl()) + return APValue(Func->getDecl(), CharUnits::fromQuantity(Offset), {}, + /*OnePastTheEnd=*/false, /*IsNull=*/false); + return APValue(Func->getExpr(), CharUnits::fromQuantity(Offset), {}, + /*OnePastTheEnd=*/false, /*IsNull=*/false); +} + +void FunctionPointer::print(llvm::raw_ostream &OS) const { + OS << "FnPtr("; + if (Func && Valid) + OS << Func->getName(); + else if (Func) + OS << reinterpret_cast<uintptr_t>(Func); + else + OS << "nullptr"; + OS << ") + " << Offset; +} + +} // namespace interp +} // namespace clang diff --git a/clang/lib/AST/ByteCode/FunctionPointer.h b/clang/lib/AST/ByteCode/FunctionPointer.h index c9bdfbee55441a..e2b45b2344fdce 100644 --- a/clang/lib/AST/ByteCode/FunctionPointer.h +++ b/clang/lib/AST/ByteCode/FunctionPointer.h @@ -11,25 +11,29 @@ #include "Function.h" #include "Primitives.h" -#include "clang/AST/APValue.h" namespace clang { class ASTContext; +class APValue; namespace interp { class FunctionPointer final { private: const Function *Func; + uint64_t Offset; bool Valid; public: FunctionPointer() = default; - FunctionPointer(const Function *Func) : Func(Func), Valid(true) {} + FunctionPointer(const Function *Func, uint64_t Offset = 0) + : Func(Func), Offset(Offset), Valid(true) {} FunctionPointer(uintptr_t IntVal, const Descriptor *Desc = nullptr) - : Func(reinterpret_cast<const Function *>(IntVal)), Valid(false) {} + : Func(reinterpret_cast<const Function *>(IntVal)), Offset(0), + Valid(false) {} const Function *getFunction() const { return Func; } + uint64_t getOffset() const { return Offset; } bool isZero() const { return !Func; } bool isValid() const { return Valid; } bool isWeak() const { @@ -39,33 +43,8 @@ class FunctionPointer final { return Func->getDecl()->isWeak(); } - APValue toAPValue(const ASTContext &) const { - if (!Func) - return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {}, - /*OnePastTheEnd=*/false, /*IsNull=*/true); - - if (!Valid) - return APValue(static_cast<Expr *>(nullptr), - CharUnits::fromQuantity(getIntegerRepresentation()), {}, - /*OnePastTheEnd=*/false, /*IsNull=*/false); - - if (Func->getDecl()) - return APValue(Func->getDecl(), CharUnits::Zero(), {}, - /*OnePastTheEnd=*/false, /*IsNull=*/false); - return APValue(Func->getExpr(), CharUnits::Zero(), {}, - /*OnePastTheEnd=*/false, /*IsNull=*/false); - } - - void print(llvm::raw_ostream &OS) const { - OS << "FnPtr("; - if (Func && Valid) - OS << Func->getName(); - else if (Func) - OS << reinterpret_cast<uintptr_t>(Func); - else - OS << "nullptr"; - OS << ")"; - } + APValue toAPValue(const ASTContext &) const; + void print(llvm::raw_ostream &OS) const; std::string toDiagnosticString(const ASTContext &Ctx) const { if (!Func) @@ -79,7 +58,7 @@ class FunctionPointer final { } ComparisonCategoryResult compare(const FunctionPointer &RHS) const { - if (Func == RHS.Func) + if (Func == RHS.Func && Offset == RHS.Offset) return ComparisonCategoryResult::Equal; return ComparisonCategoryResult::Unordered; } diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h index d8629881abc685..fd4406c0db2b88 100644 --- a/clang/lib/AST/ByteCode/Interp.h +++ b/clang/lib/AST/ByteCode/Interp.h @@ -1857,8 +1857,23 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset, else S.Stk.push<Pointer>(V - O, Ptr.asIntPointer().Desc); return true; + } else if (Ptr.isFunctionPointer()) { + uint64_t O = static_cast<uint64_t>(Offset); + uint64_t N; + if constexpr (Op == ArithOp::Add) + N = Ptr.getByteOffset() + O; + else + N = Ptr.getByteOffset() - O; + + if (N > 1) + S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index) + << N << /*non-array*/ true << 0; + S.Stk.push<Pointer>(Ptr.asFunctionPointer().getFunction(), N); + return true; } + assert(Ptr.isBlockPointer()); + uint64_t MaxIndex = static_cast<uint64_t>(Ptr.getNumElems()); uint64_t Index; if (Ptr.isOnePastEnd()) @@ -2024,10 +2039,15 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC) { return true; } - T A = LHS.isElementPastEnd() ? T::from(LHS.getNumElems()) - : T::from(LHS.getIndex()); - T B = RHS.isElementPastEnd() ? T::from(RHS.getNumElems()) - : T::from(RHS.getIndex()); + T A = LHS.isBlockPointer() + ? (LHS.isElementPastEnd() ? T::from(LHS.getNumElems()) + : T::from(LHS.getIndex())) + : T::from(LHS.getIntegerRepresentation()); + T B = RHS.isBlockPointer() + ? (RHS.isElementPastEnd() ? T::from(RHS.getNumElems()) + : T::from(RHS.getIndex())) + : T::from(RHS.getIntegerRepresentation()); + return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, A.bitWidth(), A, B); } @@ -2905,8 +2925,15 @@ inline bool DecayPtr(InterpState &S, CodePtr OpPC) { if constexpr (std::is_same_v<FromT, FunctionPointer> && std::is_same_v<ToT, Pointer>) { - S.Stk.push<Pointer>(OldPtr.getFunction()); + S.Stk.push<Pointer>(OldPtr.getFunction(), OldPtr.getOffset()); return true; + } else if constexpr (std::is_same_v<FromT, Pointer> && + std::is_same_v<ToT, FunctionPointer>) { + if (OldPtr.isFunctionPointer()) { + S.Stk.push<FunctionPointer>(OldPtr.asFunctionPointer().getFunction(), + OldPtr.getByteOffset()); + return true; + } } S.Stk.push<ToT>(ToT(OldPtr.getIntegerRepresentation(), nullptr)); diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h index ba30449977376b..27ac33616f5a8b 100644 --- a/clang/lib/AST/ByteCode/Pointer.h +++ b/clang/lib/AST/ByteCode/Pointer.h @@ -137,7 +137,7 @@ class Pointer { if (isIntegralPointer()) return asIntPointer().Value + (Offset * elemSize()); if (isFunctionPointer()) - return asFunctionPointer().getIntegerRepresentation(); + return asFunctionPointer().getIntegerRepresentation() + Offset; return reinterpret_cast<uint64_t>(asBlockPointer().Pointee) + Offset; } @@ -551,7 +551,7 @@ class Pointer { } /// Returns the byte offset from the start. - unsigned getByteOffset() const { + uint64_t getByteOffset() const { if (isIntegralPointer()) return asIntPointer().Value + Offset; if (isOnePastEnd()) @@ -614,6 +614,8 @@ class Pointer { /// Checks if the pointer is pointing to a zero-size array. bool isZeroSizeArray() const { + if (isFunctionPointer()) + return false; if (const auto *Desc = getFieldDesc()) return Desc->isZeroSizeArray(); return false; diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index 041252b6830e0a..6195a16c2c68db 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -72,6 +72,7 @@ add_clang_library(clangAST ByteCode/EvalEmitter.cpp ByteCode/Frame.cpp ByteCode/Function.cpp + ByteCode/FunctionPointer.cpp ByteCode/InterpBuiltin.cpp ByteCode/Floating.cpp ByteCode/EvaluationResult.cpp diff --git a/clang/test/AST/ByteCode/c.c b/clang/test/AST/ByteCode/c.c index b38259d41130eb..60f4d6ad1b2967 100644 --- a/clang/test/AST/ByteCode/c.c +++ b/clang/test/AST/ByteCode/c.c @@ -297,3 +297,19 @@ void T1(void) { enum teste1 test1f(void), (*test1)(void) = test1f; // pedantic-warning {{ISO C forbids forward references to 'enum' types}} enum teste1 { TEST1 }; + + +void func(void) { + _Static_assert(func + 1 - func == 1, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \ + // pedantic-warning {{arithmetic on pointers to the function type}} \ + // pedantic-warning {{not an integer constant expression}} + _Static_assert(func + 0xdead000000000000UL - 0xdead000000000000UL == func, ""); // pedantic-warning 2{{arithmetic on a pointer to the function type}} \ + // pedantic-warning {{not an integer constant expression}} \ + // pedantic-note {{cannot refer to element 16045481047390945280 of non-array object in a constant expression}} + _Static_assert(func + 1 != func, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \ + // pedantic-warning {{expression is not an integer constant expression}} + func + 0xdead000000000000UL; // all-warning {{expression result unused}} \ + // pedantic-warning {{arithmetic on a pointer to the function type}} + func - 0xdead000000000000UL; // all-warning {{expression result unused}} \ + // pedantic-warning {{arithmetic on a pointer to the function type}} +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits