Skip to content

Commit f70ccda

Browse files
authored
[clang][bytecode][NFC] Move Call ops into Interp.cpp (#107104)
They are quite long and not templated.
1 parent 7852ebc commit f70ccda

File tree

2 files changed

+246
-237
lines changed

2 files changed

+246
-237
lines changed

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,241 @@ void diagnoseEnumValue(InterpState &S, CodePtr OpPC, const EnumDecl *ED,
986986
}
987987
}
988988

989+
bool CallVar(InterpState &S, CodePtr OpPC, const Function *Func,
990+
uint32_t VarArgSize) {
991+
if (Func->hasThisPointer()) {
992+
size_t ArgSize = Func->getArgSize() + VarArgSize;
993+
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
994+
const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
995+
996+
// If the current function is a lambda static invoker and
997+
// the function we're about to call is a lambda call operator,
998+
// skip the CheckInvoke, since the ThisPtr is a null pointer
999+
// anyway.
1000+
if (!(S.Current->getFunction() &&
1001+
S.Current->getFunction()->isLambdaStaticInvoker() &&
1002+
Func->isLambdaCallOperator())) {
1003+
if (!CheckInvoke(S, OpPC, ThisPtr))
1004+
return false;
1005+
}
1006+
1007+
if (S.checkingPotentialConstantExpression())
1008+
return false;
1009+
}
1010+
1011+
if (!CheckCallable(S, OpPC, Func))
1012+
return false;
1013+
1014+
if (!CheckCallDepth(S, OpPC))
1015+
return false;
1016+
1017+
auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
1018+
InterpFrame *FrameBefore = S.Current;
1019+
S.Current = NewFrame.get();
1020+
1021+
APValue CallResult;
1022+
// Note that we cannot assert(CallResult.hasValue()) here since
1023+
// Ret() above only sets the APValue if the curent frame doesn't
1024+
// have a caller set.
1025+
if (Interpret(S, CallResult)) {
1026+
NewFrame.release(); // Frame was delete'd already.
1027+
assert(S.Current == FrameBefore);
1028+
return true;
1029+
}
1030+
1031+
// Interpreting the function failed somehow. Reset to
1032+
// previous state.
1033+
S.Current = FrameBefore;
1034+
return false;
1035+
}
1036+
1037+
bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
1038+
uint32_t VarArgSize) {
1039+
if (Func->hasThisPointer()) {
1040+
size_t ArgSize = Func->getArgSize() + VarArgSize;
1041+
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
1042+
1043+
const Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
1044+
1045+
// If the current function is a lambda static invoker and
1046+
// the function we're about to call is a lambda call operator,
1047+
// skip the CheckInvoke, since the ThisPtr is a null pointer
1048+
// anyway.
1049+
if (S.Current->getFunction() &&
1050+
S.Current->getFunction()->isLambdaStaticInvoker() &&
1051+
Func->isLambdaCallOperator()) {
1052+
assert(ThisPtr.isZero());
1053+
} else {
1054+
if (!CheckInvoke(S, OpPC, ThisPtr))
1055+
return false;
1056+
}
1057+
}
1058+
1059+
if (!CheckCallable(S, OpPC, Func))
1060+
return false;
1061+
1062+
// FIXME: The isConstructor() check here is not always right. The current
1063+
// constant evaluator is somewhat inconsistent in when it allows a function
1064+
// call when checking for a constant expression.
1065+
if (Func->hasThisPointer() && S.checkingPotentialConstantExpression() &&
1066+
!Func->isConstructor())
1067+
return false;
1068+
1069+
if (!CheckCallDepth(S, OpPC))
1070+
return false;
1071+
1072+
auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
1073+
InterpFrame *FrameBefore = S.Current;
1074+
S.Current = NewFrame.get();
1075+
1076+
APValue CallResult;
1077+
// Note that we cannot assert(CallResult.hasValue()) here since
1078+
// Ret() above only sets the APValue if the curent frame doesn't
1079+
// have a caller set.
1080+
if (Interpret(S, CallResult)) {
1081+
NewFrame.release(); // Frame was delete'd already.
1082+
assert(S.Current == FrameBefore);
1083+
return true;
1084+
}
1085+
1086+
// Interpreting the function failed somehow. Reset to
1087+
// previous state.
1088+
S.Current = FrameBefore;
1089+
return false;
1090+
}
1091+
1092+
bool CallVirt(InterpState &S, CodePtr OpPC, const Function *Func,
1093+
uint32_t VarArgSize) {
1094+
assert(Func->hasThisPointer());
1095+
assert(Func->isVirtual());
1096+
size_t ArgSize = Func->getArgSize() + VarArgSize;
1097+
size_t ThisOffset = ArgSize - (Func->hasRVO() ? primSize(PT_Ptr) : 0);
1098+
Pointer &ThisPtr = S.Stk.peek<Pointer>(ThisOffset);
1099+
1100+
const CXXRecordDecl *DynamicDecl = nullptr;
1101+
{
1102+
Pointer TypePtr = ThisPtr;
1103+
while (TypePtr.isBaseClass())
1104+
TypePtr = TypePtr.getBase();
1105+
1106+
QualType DynamicType = TypePtr.getType();
1107+
if (DynamicType->isPointerType() || DynamicType->isReferenceType())
1108+
DynamicDecl = DynamicType->getPointeeCXXRecordDecl();
1109+
else
1110+
DynamicDecl = DynamicType->getAsCXXRecordDecl();
1111+
}
1112+
assert(DynamicDecl);
1113+
1114+
const auto *StaticDecl = cast<CXXRecordDecl>(Func->getParentDecl());
1115+
const auto *InitialFunction = cast<CXXMethodDecl>(Func->getDecl());
1116+
const CXXMethodDecl *Overrider = S.getContext().getOverridingFunction(
1117+
DynamicDecl, StaticDecl, InitialFunction);
1118+
1119+
if (Overrider != InitialFunction) {
1120+
// DR1872: An instantiated virtual constexpr function can't be called in a
1121+
// constant expression (prior to C++20). We can still constant-fold such a
1122+
// call.
1123+
if (!S.getLangOpts().CPlusPlus20 && Overrider->isVirtual()) {
1124+
const Expr *E = S.Current->getExpr(OpPC);
1125+
S.CCEDiag(E, diag::note_constexpr_virtual_call) << E->getSourceRange();
1126+
}
1127+
1128+
Func = S.getContext().getOrCreateFunction(Overrider);
1129+
1130+
const CXXRecordDecl *ThisFieldDecl =
1131+
ThisPtr.getFieldDesc()->getType()->getAsCXXRecordDecl();
1132+
if (Func->getParentDecl()->isDerivedFrom(ThisFieldDecl)) {
1133+
// If the function we call is further DOWN the hierarchy than the
1134+
// FieldDesc of our pointer, just go up the hierarchy of this field
1135+
// the furthest we can go.
1136+
while (ThisPtr.isBaseClass())
1137+
ThisPtr = ThisPtr.getBase();
1138+
}
1139+
}
1140+
1141+
if (!Call(S, OpPC, Func, VarArgSize))
1142+
return false;
1143+
1144+
// Covariant return types. The return type of Overrider is a pointer
1145+
// or reference to a class type.
1146+
if (Overrider != InitialFunction &&
1147+
Overrider->getReturnType()->isPointerOrReferenceType() &&
1148+
InitialFunction->getReturnType()->isPointerOrReferenceType()) {
1149+
QualType OverriderPointeeType =
1150+
Overrider->getReturnType()->getPointeeType();
1151+
QualType InitialPointeeType =
1152+
InitialFunction->getReturnType()->getPointeeType();
1153+
// We've called Overrider above, but calling code expects us to return what
1154+
// InitialFunction returned. According to the rules for covariant return
1155+
// types, what InitialFunction returns needs to be a base class of what
1156+
// Overrider returns. So, we need to do an upcast here.
1157+
unsigned Offset = S.getContext().collectBaseOffset(
1158+
InitialPointeeType->getAsRecordDecl(),
1159+
OverriderPointeeType->getAsRecordDecl());
1160+
return GetPtrBasePop(S, OpPC, Offset);
1161+
}
1162+
1163+
return true;
1164+
}
1165+
1166+
bool CallBI(InterpState &S, CodePtr &PC, const Function *Func,
1167+
const CallExpr *CE) {
1168+
auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC);
1169+
1170+
InterpFrame *FrameBefore = S.Current;
1171+
S.Current = NewFrame.get();
1172+
1173+
if (InterpretBuiltin(S, PC, Func, CE)) {
1174+
NewFrame.release();
1175+
return true;
1176+
}
1177+
S.Current = FrameBefore;
1178+
return false;
1179+
}
1180+
1181+
bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
1182+
const CallExpr *CE) {
1183+
const FunctionPointer &FuncPtr = S.Stk.pop<FunctionPointer>();
1184+
1185+
const Function *F = FuncPtr.getFunction();
1186+
if (!F) {
1187+
const auto *E = cast<CallExpr>(S.Current->getExpr(OpPC));
1188+
S.FFDiag(E, diag::note_constexpr_null_callee)
1189+
<< const_cast<Expr *>(E->getCallee()) << E->getSourceRange();
1190+
return false;
1191+
}
1192+
1193+
if (!FuncPtr.isValid() || !F->getDecl())
1194+
return Invalid(S, OpPC);
1195+
1196+
assert(F);
1197+
1198+
// This happens when the call expression has been cast to
1199+
// something else, but we don't support that.
1200+
if (S.Ctx.classify(F->getDecl()->getReturnType()) !=
1201+
S.Ctx.classify(CE->getType()))
1202+
return false;
1203+
1204+
// Check argument nullability state.
1205+
if (F->hasNonNullAttr()) {
1206+
if (!CheckNonNullArgs(S, OpPC, F, CE, ArgSize))
1207+
return false;
1208+
}
1209+
1210+
assert(ArgSize >= F->getWrittenArgSize());
1211+
uint32_t VarArgSize = ArgSize - F->getWrittenArgSize();
1212+
1213+
// We need to do this explicitly here since we don't have the necessary
1214+
// information to do it automatically.
1215+
if (F->isThisPointerExplicit())
1216+
VarArgSize -= align(primSize(PT_Ptr));
1217+
1218+
if (F->isVirtual())
1219+
return CallVirt(S, OpPC, F, VarArgSize);
1220+
1221+
return Call(S, OpPC, F, VarArgSize);
1222+
}
1223+
9891224
bool Interpret(InterpState &S, APValue &Result) {
9901225
// The current stack frame when we started Interpret().
9911226
// This is being used by the ops to determine wheter

0 commit comments

Comments
 (0)