Skip to content

Commit db94852

Browse files
authored
[clang][bytecode] Allow adding offsets to function pointers (#105641)
Convert them to Pointers, do the offset calculation and then convert them back to function pointers.
1 parent c82f797 commit db94852

File tree

7 files changed

+139
-45
lines changed

7 files changed

+139
-45
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -885,34 +885,60 @@ bool Compiler<Emitter>::VisitPointerArithBinOp(const BinaryOperator *E) {
885885
if (!LT || !RT)
886886
return false;
887887

888+
// Visit the given pointer expression and optionally convert to a PT_Ptr.
889+
auto visitAsPointer = [&](const Expr *E, PrimType T) -> bool {
890+
if (!this->visit(E))
891+
return false;
892+
if (T != PT_Ptr)
893+
return this->emitDecayPtr(T, PT_Ptr, E);
894+
return true;
895+
};
896+
888897
if (LHS->getType()->isPointerType() && RHS->getType()->isPointerType()) {
889898
if (Op != BO_Sub)
890899
return false;
891900

892901
assert(E->getType()->isIntegerType());
893-
if (!visit(RHS) || !visit(LHS))
902+
if (!visitAsPointer(RHS, *RT) || !visitAsPointer(LHS, *LT))
894903
return false;
895904

896905
return this->emitSubPtr(classifyPrim(E->getType()), E);
897906
}
898907

899908
PrimType OffsetType;
900909
if (LHS->getType()->isIntegerType()) {
901-
if (!visit(RHS) || !visit(LHS))
910+
if (!visitAsPointer(RHS, *RT))
911+
return false;
912+
if (!this->visit(LHS))
902913
return false;
903914
OffsetType = *LT;
904915
} else if (RHS->getType()->isIntegerType()) {
905-
if (!visit(LHS) || !visit(RHS))
916+
if (!visitAsPointer(LHS, *LT))
917+
return false;
918+
if (!this->visit(RHS))
906919
return false;
907920
OffsetType = *RT;
908921
} else {
909922
return false;
910923
}
911924

912-
if (Op == BO_Add)
913-
return this->emitAddOffset(OffsetType, E);
914-
else if (Op == BO_Sub)
915-
return this->emitSubOffset(OffsetType, E);
925+
// Do the operation and optionally transform to
926+
// result pointer type.
927+
if (Op == BO_Add) {
928+
if (!this->emitAddOffset(OffsetType, E))
929+
return false;
930+
931+
if (classifyPrim(E) != PT_Ptr)
932+
return this->emitDecayPtr(PT_Ptr, classifyPrim(E), E);
933+
return true;
934+
} else if (Op == BO_Sub) {
935+
if (!this->emitSubOffset(OffsetType, E))
936+
return false;
937+
938+
if (classifyPrim(E) != PT_Ptr)
939+
return this->emitDecayPtr(PT_Ptr, classifyPrim(E), E);
940+
return true;
941+
}
916942

917943
return false;
918944
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//===----------------------- FunctionPointer.cpp ----------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "FunctionPointer.h"
10+
11+
namespace clang {
12+
namespace interp {
13+
14+
APValue FunctionPointer::toAPValue(const ASTContext &) const {
15+
if (!Func)
16+
return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {},
17+
/*OnePastTheEnd=*/false, /*IsNull=*/true);
18+
19+
if (!Valid)
20+
return APValue(static_cast<Expr *>(nullptr),
21+
CharUnits::fromQuantity(getIntegerRepresentation()), {},
22+
/*OnePastTheEnd=*/false, /*IsNull=*/false);
23+
24+
if (Func->getDecl())
25+
return APValue(Func->getDecl(), CharUnits::fromQuantity(Offset), {},
26+
/*OnePastTheEnd=*/false, /*IsNull=*/false);
27+
return APValue(Func->getExpr(), CharUnits::fromQuantity(Offset), {},
28+
/*OnePastTheEnd=*/false, /*IsNull=*/false);
29+
}
30+
31+
void FunctionPointer::print(llvm::raw_ostream &OS) const {
32+
OS << "FnPtr(";
33+
if (Func && Valid)
34+
OS << Func->getName();
35+
else if (Func)
36+
OS << reinterpret_cast<uintptr_t>(Func);
37+
else
38+
OS << "nullptr";
39+
OS << ") + " << Offset;
40+
}
41+
42+
} // namespace interp
43+
} // namespace clang

clang/lib/AST/ByteCode/FunctionPointer.h

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,29 @@
1111

1212
#include "Function.h"
1313
#include "Primitives.h"
14-
#include "clang/AST/APValue.h"
1514

1615
namespace clang {
1716
class ASTContext;
17+
class APValue;
1818
namespace interp {
1919

2020
class FunctionPointer final {
2121
private:
2222
const Function *Func;
23+
uint64_t Offset;
2324
bool Valid;
2425

2526
public:
2627
FunctionPointer() = default;
27-
FunctionPointer(const Function *Func) : Func(Func), Valid(true) {}
28+
FunctionPointer(const Function *Func, uint64_t Offset = 0)
29+
: Func(Func), Offset(Offset), Valid(true) {}
2830

2931
FunctionPointer(uintptr_t IntVal, const Descriptor *Desc = nullptr)
30-
: Func(reinterpret_cast<const Function *>(IntVal)), Valid(false) {}
32+
: Func(reinterpret_cast<const Function *>(IntVal)), Offset(0),
33+
Valid(false) {}
3134

3235
const Function *getFunction() const { return Func; }
36+
uint64_t getOffset() const { return Offset; }
3337
bool isZero() const { return !Func; }
3438
bool isValid() const { return Valid; }
3539
bool isWeak() const {
@@ -39,33 +43,8 @@ class FunctionPointer final {
3943
return Func->getDecl()->isWeak();
4044
}
4145

42-
APValue toAPValue(const ASTContext &) const {
43-
if (!Func)
44-
return APValue(static_cast<Expr *>(nullptr), CharUnits::Zero(), {},
45-
/*OnePastTheEnd=*/false, /*IsNull=*/true);
46-
47-
if (!Valid)
48-
return APValue(static_cast<Expr *>(nullptr),
49-
CharUnits::fromQuantity(getIntegerRepresentation()), {},
50-
/*OnePastTheEnd=*/false, /*IsNull=*/false);
51-
52-
if (Func->getDecl())
53-
return APValue(Func->getDecl(), CharUnits::Zero(), {},
54-
/*OnePastTheEnd=*/false, /*IsNull=*/false);
55-
return APValue(Func->getExpr(), CharUnits::Zero(), {},
56-
/*OnePastTheEnd=*/false, /*IsNull=*/false);
57-
}
58-
59-
void print(llvm::raw_ostream &OS) const {
60-
OS << "FnPtr(";
61-
if (Func && Valid)
62-
OS << Func->getName();
63-
else if (Func)
64-
OS << reinterpret_cast<uintptr_t>(Func);
65-
else
66-
OS << "nullptr";
67-
OS << ")";
68-
}
46+
APValue toAPValue(const ASTContext &) const;
47+
void print(llvm::raw_ostream &OS) const;
6948

7049
std::string toDiagnosticString(const ASTContext &Ctx) const {
7150
if (!Func)
@@ -79,7 +58,7 @@ class FunctionPointer final {
7958
}
8059

8160
ComparisonCategoryResult compare(const FunctionPointer &RHS) const {
82-
if (Func == RHS.Func)
61+
if (Func == RHS.Func && Offset == RHS.Offset)
8362
return ComparisonCategoryResult::Equal;
8463
return ComparisonCategoryResult::Unordered;
8564
}

clang/lib/AST/ByteCode/Interp.h

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1857,8 +1857,23 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
18571857
else
18581858
S.Stk.push<Pointer>(V - O, Ptr.asIntPointer().Desc);
18591859
return true;
1860+
} else if (Ptr.isFunctionPointer()) {
1861+
uint64_t O = static_cast<uint64_t>(Offset);
1862+
uint64_t N;
1863+
if constexpr (Op == ArithOp::Add)
1864+
N = Ptr.getByteOffset() + O;
1865+
else
1866+
N = Ptr.getByteOffset() - O;
1867+
1868+
if (N > 1)
1869+
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
1870+
<< N << /*non-array*/ true << 0;
1871+
S.Stk.push<Pointer>(Ptr.asFunctionPointer().getFunction(), N);
1872+
return true;
18601873
}
18611874

1875+
assert(Ptr.isBlockPointer());
1876+
18621877
uint64_t MaxIndex = static_cast<uint64_t>(Ptr.getNumElems());
18631878
uint64_t Index;
18641879
if (Ptr.isOnePastEnd())
@@ -2024,10 +2039,15 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC) {
20242039
return true;
20252040
}
20262041

2027-
T A = LHS.isElementPastEnd() ? T::from(LHS.getNumElems())
2028-
: T::from(LHS.getIndex());
2029-
T B = RHS.isElementPastEnd() ? T::from(RHS.getNumElems())
2030-
: T::from(RHS.getIndex());
2042+
T A = LHS.isBlockPointer()
2043+
? (LHS.isElementPastEnd() ? T::from(LHS.getNumElems())
2044+
: T::from(LHS.getIndex()))
2045+
: T::from(LHS.getIntegerRepresentation());
2046+
T B = RHS.isBlockPointer()
2047+
? (RHS.isElementPastEnd() ? T::from(RHS.getNumElems())
2048+
: T::from(RHS.getIndex()))
2049+
: T::from(RHS.getIntegerRepresentation());
2050+
20312051
return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, A.bitWidth(), A, B);
20322052
}
20332053

@@ -2905,8 +2925,15 @@ inline bool DecayPtr(InterpState &S, CodePtr OpPC) {
29052925

29062926
if constexpr (std::is_same_v<FromT, FunctionPointer> &&
29072927
std::is_same_v<ToT, Pointer>) {
2908-
S.Stk.push<Pointer>(OldPtr.getFunction());
2928+
S.Stk.push<Pointer>(OldPtr.getFunction(), OldPtr.getOffset());
29092929
return true;
2930+
} else if constexpr (std::is_same_v<FromT, Pointer> &&
2931+
std::is_same_v<ToT, FunctionPointer>) {
2932+
if (OldPtr.isFunctionPointer()) {
2933+
S.Stk.push<FunctionPointer>(OldPtr.asFunctionPointer().getFunction(),
2934+
OldPtr.getByteOffset());
2935+
return true;
2936+
}
29102937
}
29112938

29122939
S.Stk.push<ToT>(ToT(OldPtr.getIntegerRepresentation(), nullptr));

clang/lib/AST/ByteCode/Pointer.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ class Pointer {
137137
if (isIntegralPointer())
138138
return asIntPointer().Value + (Offset * elemSize());
139139
if (isFunctionPointer())
140-
return asFunctionPointer().getIntegerRepresentation();
140+
return asFunctionPointer().getIntegerRepresentation() + Offset;
141141
return reinterpret_cast<uint64_t>(asBlockPointer().Pointee) + Offset;
142142
}
143143

@@ -551,7 +551,7 @@ class Pointer {
551551
}
552552

553553
/// Returns the byte offset from the start.
554-
unsigned getByteOffset() const {
554+
uint64_t getByteOffset() const {
555555
if (isIntegralPointer())
556556
return asIntPointer().Value + Offset;
557557
if (isOnePastEnd())
@@ -614,6 +614,8 @@ class Pointer {
614614

615615
/// Checks if the pointer is pointing to a zero-size array.
616616
bool isZeroSizeArray() const {
617+
if (isFunctionPointer())
618+
return false;
617619
if (const auto *Desc = getFieldDesc())
618620
return Desc->isZeroSizeArray();
619621
return false;

clang/lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ add_clang_library(clangAST
7272
ByteCode/EvalEmitter.cpp
7373
ByteCode/Frame.cpp
7474
ByteCode/Function.cpp
75+
ByteCode/FunctionPointer.cpp
7576
ByteCode/InterpBuiltin.cpp
7677
ByteCode/Floating.cpp
7778
ByteCode/EvaluationResult.cpp

clang/test/AST/ByteCode/c.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,19 @@ void T1(void) {
297297

298298
enum teste1 test1f(void), (*test1)(void) = test1f; // pedantic-warning {{ISO C forbids forward references to 'enum' types}}
299299
enum teste1 { TEST1 };
300+
301+
302+
void func(void) {
303+
_Static_assert(func + 1 - func == 1, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \
304+
// pedantic-warning {{arithmetic on pointers to the function type}} \
305+
// pedantic-warning {{not an integer constant expression}}
306+
_Static_assert(func + 0xdead000000000000UL - 0xdead000000000000UL == func, ""); // pedantic-warning 2{{arithmetic on a pointer to the function type}} \
307+
// pedantic-warning {{not an integer constant expression}} \
308+
// pedantic-note {{cannot refer to element 16045481047390945280 of non-array object in a constant expression}}
309+
_Static_assert(func + 1 != func, ""); // pedantic-warning {{arithmetic on a pointer to the function type}} \
310+
// pedantic-warning {{expression is not an integer constant expression}}
311+
func + 0xdead000000000000UL; // all-warning {{expression result unused}} \
312+
// pedantic-warning {{arithmetic on a pointer to the function type}}
313+
func - 0xdead000000000000UL; // all-warning {{expression result unused}} \
314+
// pedantic-warning {{arithmetic on a pointer to the function type}}
315+
}

0 commit comments

Comments
 (0)