[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
//===--- Interp.h - Interpreter for the constexpr VM ------------*- 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
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// Definition of the interpreter state and entry point.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#ifndef LLVM_CLANG_AST_INTERP_INTERP_H
|
|
|
|
#define LLVM_CLANG_AST_INTERP_INTERP_H
|
|
|
|
|
|
|
|
#include "Function.h"
|
|
|
|
#include "InterpFrame.h"
|
|
|
|
#include "InterpStack.h"
|
|
|
|
#include "InterpState.h"
|
|
|
|
#include "Opcode.h"
|
|
|
|
#include "PrimType.h"
|
|
|
|
#include "Program.h"
|
|
|
|
#include "State.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
|
|
#include "clang/AST/ASTDiagnostic.h"
|
|
|
|
#include "clang/AST/CXXInheritance.h"
|
|
|
|
#include "clang/AST/Expr.h"
|
|
|
|
#include "llvm/ADT/APFloat.h"
|
|
|
|
#include "llvm/ADT/APSInt.h"
|
|
|
|
#include "llvm/Support/Endian.h"
|
[Clang interpreter] Avoid storing pointers at unaligned locations
The Clang interpreter's bytecode uses a packed stream of bytes
representation, but also wants to have some opcodes take pointers as
arguments, which are currently embedded in the bytecode directly.
However, CHERI, and thus Arm's upcoming experimental Morello prototype,
provide spatial memory safety for C/C++ by implementing language-level
(and sub-language-level) pointers as capabilities, which track bounds,
permissions and validity in hardware. This uses tagged memory with a
single tag bit at every capability-aligned address, and so storing
pointers to unaligned addresses results in the tag being stripped,
leading to a tag fault when the pointer is ultimately dereferenced at a
later point.
In order to support a stricter C/C++ implementation like CHERI, we no
longer store pointers directly in the bytecode, instead storing them in
a table and embedding the index in the bytecode.
Reviewed By: nand
Differential Revision: https://reviews.llvm.org/D97606
2021-07-28 14:49:37 +01:00
|
|
|
#include <limits>
|
|
|
|
#include <type_traits>
|
|
|
|
#include <vector>
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace interp {
|
|
|
|
|
|
|
|
using APInt = llvm::APInt;
|
|
|
|
using APSInt = llvm::APSInt;
|
|
|
|
|
2021-08-31 12:31:24 +05:30
|
|
|
/// Convert a value to an APValue.
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
template <typename T> bool ReturnValue(const T &V, APValue &R) {
|
|
|
|
R = V.toAPValue();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Checks if the variable has externally defined storage.
|
|
|
|
bool CheckExtern(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
|
|
|
|
|
|
|
|
/// Checks if the array is offsetable.
|
|
|
|
bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
|
|
|
|
|
2021-08-31 12:31:24 +05:30
|
|
|
/// Checks if a pointer is live and accessible.
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
|
|
|
AccessKinds AK);
|
|
|
|
/// Checks if a pointer is null.
|
|
|
|
bool CheckNull(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
|
|
|
CheckSubobjectKind CSK);
|
|
|
|
|
|
|
|
/// Checks if a pointer is in range.
|
|
|
|
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
|
|
|
AccessKinds AK);
|
|
|
|
|
|
|
|
/// Checks if a field from which a pointer is going to be derived is valid.
|
|
|
|
bool CheckRange(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
|
|
|
|
CheckSubobjectKind CSK);
|
|
|
|
|
|
|
|
/// Checks if a pointer points to const storage.
|
|
|
|
bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
|
|
|
|
|
|
|
|
/// Checks if a pointer points to a mutable field.
|
|
|
|
bool CheckMutable(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
|
|
|
|
|
|
|
|
/// Checks if a value can be loaded from a block.
|
|
|
|
bool CheckLoad(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, Function *F);
|
|
|
|
|
|
|
|
/// Checks the 'this' pointer.
|
|
|
|
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
|
|
|
|
|
|
|
|
/// Checks if a method is pure virtual.
|
|
|
|
bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD);
|
|
|
|
|
|
|
|
template <typename T> inline bool IsTrue(const T &V) { return !V.isZero(); }
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Add, Sub, Mul
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <typename T, bool (*OpFW)(T, T, unsigned, T *),
|
|
|
|
template <typename U> class OpAP>
|
|
|
|
bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS,
|
|
|
|
const T &RHS) {
|
|
|
|
// Fast path - add the numbers with fixed width.
|
|
|
|
T Result;
|
|
|
|
if (!OpFW(LHS, RHS, Bits, &Result)) {
|
|
|
|
S.Stk.push<T>(Result);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If for some reason evaluation continues, use the truncated results.
|
|
|
|
S.Stk.push<T>(Result);
|
|
|
|
|
|
|
|
// Slow path - compute the result using another bit of precision.
|
|
|
|
APSInt Value = OpAP<APSInt>()(LHS.toAPSInt(Bits), RHS.toAPSInt(Bits));
|
|
|
|
|
|
|
|
// Report undefined behaviour, stopping if required.
|
|
|
|
const Expr *E = S.Current->getExpr(OpPC);
|
|
|
|
QualType Type = E->getType();
|
|
|
|
if (S.checkingForUndefinedBehavior()) {
|
2021-06-09 11:06:23 +01:00
|
|
|
SmallString<32> Trunc;
|
|
|
|
Value.trunc(Result.bitWidth()).toString(Trunc, 10);
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
auto Loc = E->getExprLoc();
|
|
|
|
S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type;
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type;
|
|
|
|
return S.noteUndefinedBehavior();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Add(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T &RHS = S.Stk.pop<T>();
|
|
|
|
const T &LHS = S.Stk.pop<T>();
|
|
|
|
const unsigned Bits = RHS.bitWidth() + 1;
|
|
|
|
return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Sub(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T &RHS = S.Stk.pop<T>();
|
|
|
|
const T &LHS = S.Stk.pop<T>();
|
|
|
|
const unsigned Bits = RHS.bitWidth() + 1;
|
|
|
|
return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Mul(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T &RHS = S.Stk.pop<T>();
|
|
|
|
const T &LHS = S.Stk.pop<T>();
|
|
|
|
const unsigned Bits = RHS.bitWidth() * 2;
|
|
|
|
return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
2022-08-09 08:08:48 +02:00
|
|
|
// Inv
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Inv(InterpState &S, CodePtr OpPC) {
|
|
|
|
using BoolT = PrimConv<PT_Bool>::T;
|
|
|
|
const T &Val = S.Stk.pop<T>();
|
|
|
|
const unsigned Bits = Val.bitWidth();
|
|
|
|
Boolean R;
|
|
|
|
Boolean::inv(BoolT::from(Val, Bits), &R);
|
|
|
|
|
|
|
|
S.Stk.push<BoolT>(R);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Neg
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Neg(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T &Val = S.Stk.pop<T>();
|
|
|
|
T Result;
|
|
|
|
T::neg(Val, &Result);
|
|
|
|
|
|
|
|
S.Stk.push<T>(Result);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
// EQ, NE, GT, GE, LT, LE
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
using CompareFn = llvm::function_ref<bool(ComparisonCategoryResult)>;
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) {
|
|
|
|
using BoolT = PrimConv<PT_Bool>::T;
|
|
|
|
const T &RHS = S.Stk.pop<T>();
|
|
|
|
const T &LHS = S.Stk.pop<T>();
|
|
|
|
S.Stk.push<BoolT>(BoolT::from(Fn(LHS.compare(RHS))));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) {
|
|
|
|
return CmpHelper<T>(S, OpPC, Fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
|
|
|
inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
|
|
|
|
using BoolT = PrimConv<PT_Bool>::T;
|
|
|
|
const Pointer &RHS = S.Stk.pop<Pointer>();
|
|
|
|
const Pointer &LHS = S.Stk.pop<Pointer>();
|
|
|
|
|
|
|
|
if (!Pointer::hasSameBase(LHS, RHS)) {
|
|
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
|
|
S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
unsigned VL = LHS.getByteOffset();
|
|
|
|
unsigned VR = RHS.getByteOffset();
|
|
|
|
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <>
|
|
|
|
inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
|
|
|
|
using BoolT = PrimConv<PT_Bool>::T;
|
|
|
|
const Pointer &RHS = S.Stk.pop<Pointer>();
|
|
|
|
const Pointer &LHS = S.Stk.pop<Pointer>();
|
|
|
|
|
2019-12-16 17:40:03 -08:00
|
|
|
if (LHS.isZero() && RHS.isZero()) {
|
|
|
|
S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Equal)));
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!Pointer::hasSameBase(LHS, RHS)) {
|
|
|
|
S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Unordered)));
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
unsigned VL = LHS.getByteOffset();
|
|
|
|
unsigned VR = RHS.getByteOffset();
|
|
|
|
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool EQ(InterpState &S, CodePtr OpPC) {
|
|
|
|
return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
|
|
|
|
return R == ComparisonCategoryResult::Equal;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool NE(InterpState &S, CodePtr OpPC) {
|
|
|
|
return CmpHelperEQ<T>(S, OpPC, [](ComparisonCategoryResult R) {
|
|
|
|
return R != ComparisonCategoryResult::Equal;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool LT(InterpState &S, CodePtr OpPC) {
|
|
|
|
return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
|
|
|
|
return R == ComparisonCategoryResult::Less;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool LE(InterpState &S, CodePtr OpPC) {
|
|
|
|
return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
|
|
|
|
return R == ComparisonCategoryResult::Less ||
|
|
|
|
R == ComparisonCategoryResult::Equal;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool GT(InterpState &S, CodePtr OpPC) {
|
|
|
|
return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
|
|
|
|
return R == ComparisonCategoryResult::Greater;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool GE(InterpState &S, CodePtr OpPC) {
|
|
|
|
return CmpHelper<T>(S, OpPC, [](ComparisonCategoryResult R) {
|
|
|
|
return R == ComparisonCategoryResult::Greater ||
|
|
|
|
R == ComparisonCategoryResult::Equal;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// InRange
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InRange(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T RHS = S.Stk.pop<T>();
|
|
|
|
const T LHS = S.Stk.pop<T>();
|
|
|
|
const T Value = S.Stk.pop<T>();
|
|
|
|
|
|
|
|
S.Stk.push<bool>(LHS <= Value && Value <= RHS);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Dup, Pop, Test
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Dup(InterpState &S, CodePtr OpPC) {
|
|
|
|
S.Stk.push<T>(S.Stk.peek<T>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Pop(InterpState &S, CodePtr OpPC) {
|
|
|
|
S.Stk.pop<T>();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Const
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Const(InterpState &S, CodePtr OpPC, const T &Arg) {
|
|
|
|
S.Stk.push<T>(Arg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Get/Set Local/Param/Global/This
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
S.Stk.push<T>(S.Current->getLocal<T>(I));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool SetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
S.Current->setLocal<T>(I, S.Stk.pop<T>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool GetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
if (S.checkingPotentialConstantExpression()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
S.Stk.push<T>(S.Current->getParam<T>(I));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
S.Current->setParam<T>(I, S.Stk.pop<T>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
const Pointer &Obj = S.Stk.peek<Pointer>();
|
|
|
|
if (!CheckNull(S, OpPC, Obj, CSK_Field))
|
|
|
|
return false;
|
|
|
|
if (!CheckRange(S, OpPC, Obj, CSK_Field))
|
|
|
|
return false;
|
|
|
|
const Pointer &Field = Obj.atField(I);
|
|
|
|
if (!CheckLoad(S, OpPC, Field))
|
|
|
|
return false;
|
|
|
|
S.Stk.push<T>(Field.deref<T>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Obj = S.Stk.peek<Pointer>();
|
|
|
|
if (!CheckNull(S, OpPC, Obj, CSK_Field))
|
|
|
|
return false;
|
|
|
|
if (!CheckRange(S, OpPC, Obj, CSK_Field))
|
|
|
|
return false;
|
|
|
|
const Pointer &Field = Obj.atField(I);
|
|
|
|
if (!CheckStore(S, OpPC, Field))
|
|
|
|
return false;
|
|
|
|
Field.deref<T>() = Value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
const Pointer &Obj = S.Stk.pop<Pointer>();
|
|
|
|
if (!CheckNull(S, OpPC, Obj, CSK_Field))
|
|
|
|
return false;
|
|
|
|
if (!CheckRange(S, OpPC, Obj, CSK_Field))
|
|
|
|
return false;
|
|
|
|
const Pointer &Field = Obj.atField(I);
|
|
|
|
if (!CheckLoad(S, OpPC, Field))
|
|
|
|
return false;
|
|
|
|
S.Stk.push<T>(Field.deref<T>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool GetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
if (S.checkingPotentialConstantExpression())
|
|
|
|
return false;
|
|
|
|
const Pointer &This = S.Current->getThis();
|
|
|
|
if (!CheckThis(S, OpPC, This))
|
|
|
|
return false;
|
|
|
|
const Pointer &Field = This.atField(I);
|
|
|
|
if (!CheckLoad(S, OpPC, Field))
|
|
|
|
return false;
|
|
|
|
S.Stk.push<T>(Field.deref<T>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool SetThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
if (S.checkingPotentialConstantExpression())
|
|
|
|
return false;
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &This = S.Current->getThis();
|
|
|
|
if (!CheckThis(S, OpPC, This))
|
|
|
|
return false;
|
|
|
|
const Pointer &Field = This.atField(I);
|
|
|
|
if (!CheckStore(S, OpPC, Field))
|
|
|
|
return false;
|
|
|
|
Field.deref<T>() = Value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool GetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
auto *B = S.P.getGlobal(I);
|
|
|
|
if (B->isExtern())
|
|
|
|
return false;
|
|
|
|
S.Stk.push<T>(B->deref<T>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool SetGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
// TODO: emit warning.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
S.P.getGlobal(I)->deref<T>() = S.Stk.pop<T>();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitThisField(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
if (S.checkingPotentialConstantExpression())
|
|
|
|
return false;
|
|
|
|
const Pointer &This = S.Current->getThis();
|
|
|
|
if (!CheckThis(S, OpPC, This))
|
|
|
|
return false;
|
|
|
|
const Pointer &Field = This.atField(I);
|
|
|
|
Field.deref<T>() = S.Stk.pop<T>();
|
|
|
|
Field.initialize();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitThisBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
|
|
|
|
if (S.checkingPotentialConstantExpression())
|
|
|
|
return false;
|
|
|
|
const Pointer &This = S.Current->getThis();
|
|
|
|
if (!CheckThis(S, OpPC, This))
|
|
|
|
return false;
|
|
|
|
const Pointer &Field = This.atField(F->Offset);
|
|
|
|
const auto &Value = S.Stk.pop<T>();
|
|
|
|
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
|
|
|
|
Field.initialize();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
if (S.checkingPotentialConstantExpression())
|
|
|
|
return false;
|
|
|
|
const Pointer &This = S.Current->getThis();
|
|
|
|
if (!CheckThis(S, OpPC, This))
|
|
|
|
return false;
|
|
|
|
const Pointer &Field = This.atField(I);
|
|
|
|
Field.deref<T>() = S.Stk.pop<T>();
|
|
|
|
Field.activate();
|
|
|
|
Field.initialize();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Field = S.Stk.pop<Pointer>().atField(I);
|
|
|
|
Field.deref<T>() = Value;
|
|
|
|
Field.activate();
|
|
|
|
Field.initialize();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitBitField(InterpState &S, CodePtr OpPC, const Record::Field *F) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Field = S.Stk.pop<Pointer>().atField(F->Offset);
|
|
|
|
Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue(S.getCtx()));
|
|
|
|
Field.activate();
|
|
|
|
Field.initialize();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
const Pointer &Field = Ptr.atField(I);
|
|
|
|
Field.deref<T>() = Value;
|
|
|
|
Field.activate();
|
|
|
|
Field.initialize();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// GetPtr Local/Param/Global/Field/This
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
inline bool GetPtrLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
S.Stk.push<Pointer>(S.Current->getLocalPointer(I));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool GetPtrParam(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
if (S.checkingPotentialConstantExpression()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
S.Stk.push<Pointer>(S.Current->getParamPointer(I));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
S.Stk.push<Pointer>(S.P.getPtrGlobal(I));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
if (!CheckNull(S, OpPC, Ptr, CSK_Field))
|
|
|
|
return false;
|
|
|
|
if (!CheckExtern(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
if (!CheckRange(S, OpPC, Ptr, CSK_Field))
|
|
|
|
return false;
|
|
|
|
S.Stk.push<Pointer>(Ptr.atField(Off));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool GetPtrThisField(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;
|
|
|
|
S.Stk.push<Pointer>(This.atField(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 GetPtrBase(InterpState &S, CodePtr OpPC, uint32_t Off) {
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
if (!CheckNull(S, OpPC, Ptr, CSK_Base))
|
|
|
|
return false;
|
|
|
|
S.Stk.push<Pointer>(Ptr.atField(Off));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool GetPtrThisBase(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;
|
|
|
|
S.Stk.push<Pointer>(This.atField(Off));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool VirtBaseHelper(InterpState &S, CodePtr OpPC, const RecordDecl *Decl,
|
|
|
|
const Pointer &Ptr) {
|
|
|
|
Pointer Base = Ptr;
|
|
|
|
while (Base.isBaseClass())
|
|
|
|
Base = Base.getBase();
|
|
|
|
|
|
|
|
auto *Field = Base.getRecord()->getVirtualBase(Decl);
|
|
|
|
S.Stk.push<Pointer>(Base.atField(Field->Offset));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool GetPtrVirtBase(InterpState &S, CodePtr OpPC, const RecordDecl *D) {
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
if (!CheckNull(S, OpPC, Ptr, CSK_Base))
|
|
|
|
return false;
|
|
|
|
return VirtBaseHelper(S, OpPC, D, Ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool GetPtrThisVirtBase(InterpState &S, CodePtr OpPC,
|
|
|
|
const RecordDecl *D) {
|
|
|
|
if (S.checkingPotentialConstantExpression())
|
|
|
|
return false;
|
|
|
|
const Pointer &This = S.Current->getThis();
|
|
|
|
if (!CheckThis(S, OpPC, This))
|
|
|
|
return false;
|
|
|
|
return VirtBaseHelper(S, OpPC, D, S.Current->getThis());
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Load, Store, Init
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Load(InterpState &S, CodePtr OpPC) {
|
|
|
|
const Pointer &Ptr = S.Stk.peek<Pointer>();
|
|
|
|
if (!CheckLoad(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
S.Stk.push<T>(Ptr.deref<T>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool LoadPop(InterpState &S, CodePtr OpPC) {
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
if (!CheckLoad(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
S.Stk.push<T>(Ptr.deref<T>());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Store(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Ptr = S.Stk.peek<Pointer>();
|
|
|
|
if (!CheckStore(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
Ptr.deref<T>() = Value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool StorePop(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
if (!CheckStore(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
Ptr.deref<T>() = Value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool StoreBitField(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Ptr = S.Stk.peek<Pointer>();
|
|
|
|
if (!CheckStore(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
if (auto *FD = Ptr.getField()) {
|
|
|
|
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
|
|
|
|
} else {
|
|
|
|
Ptr.deref<T>() = Value;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
if (!CheckStore(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
if (auto *FD = Ptr.getField()) {
|
|
|
|
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
|
|
|
|
} else {
|
|
|
|
Ptr.deref<T>() = Value;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitPop(InterpState &S, CodePtr OpPC) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
if (!CheckInit(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
Ptr.initialize();
|
|
|
|
new (&Ptr.deref<T>()) T(Value);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-08-31 16:09:40 +02:00
|
|
|
/// 1) Pops the value from the stack
|
|
|
|
/// 2) Peeks a pointer and gets its index \Idx
|
|
|
|
/// 3) Sets the value on the pointer, leaving the pointer on the stack.
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Ptr = S.Stk.peek<Pointer>().atIndex(Idx);
|
|
|
|
if (!CheckInit(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
Ptr.initialize();
|
|
|
|
new (&Ptr.deref<T>()) T(Value);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-08-31 16:09:40 +02:00
|
|
|
/// The same as InitElem, but pops the pointer as well.
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
|
|
|
|
const T &Value = S.Stk.pop<T>();
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>().atIndex(Idx);
|
|
|
|
if (!CheckInit(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
Ptr.initialize();
|
|
|
|
new (&Ptr.deref<T>()) T(Value);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// AddOffset, SubOffset
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) {
|
|
|
|
// Fetch the pointer and the offset.
|
|
|
|
const T &Offset = S.Stk.pop<T>();
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex))
|
|
|
|
return false;
|
|
|
|
if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Get a version of the index comparable to the type.
|
|
|
|
T Index = T::from(Ptr.getIndex(), Offset.bitWidth());
|
|
|
|
// A zero offset does not change the pointer, but in the case of an array
|
|
|
|
// it has to be adjusted to point to the first element instead of the array.
|
|
|
|
if (Offset.isZero()) {
|
|
|
|
S.Stk.push<Pointer>(Index.isZero() ? Ptr.atIndex(0) : Ptr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Arrays of unknown bounds cannot have pointers into them.
|
|
|
|
if (!CheckArray(S, OpPC, Ptr))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Compute the largest index into the array.
|
|
|
|
unsigned MaxIndex = Ptr.getNumElems();
|
|
|
|
|
|
|
|
// Helper to report an invalid offset, computed as APSInt.
|
|
|
|
auto InvalidOffset = [&]() {
|
|
|
|
const unsigned Bits = Offset.bitWidth();
|
|
|
|
APSInt APOffset(Offset.toAPSInt().extend(Bits + 2), false);
|
|
|
|
APSInt APIndex(Index.toAPSInt().extend(Bits + 2), false);
|
|
|
|
APSInt NewIndex = Add ? (APIndex + APOffset) : (APIndex - APOffset);
|
|
|
|
S.CCEDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
|
|
|
|
<< NewIndex
|
|
|
|
<< /*array*/ static_cast<int>(!Ptr.inArray())
|
|
|
|
<< static_cast<unsigned>(MaxIndex);
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
// If the new offset would be negative, bail out.
|
|
|
|
if (Add && Offset.isNegative() && (Offset.isMin() || -Offset > Index))
|
|
|
|
return InvalidOffset();
|
|
|
|
if (!Add && Offset.isPositive() && Index < Offset)
|
|
|
|
return InvalidOffset();
|
|
|
|
|
|
|
|
// If the new offset would be out of bounds, bail out.
|
|
|
|
unsigned MaxOffset = MaxIndex - Ptr.getIndex();
|
|
|
|
if (Add && Offset.isPositive() && Offset > MaxOffset)
|
|
|
|
return InvalidOffset();
|
|
|
|
if (!Add && Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
|
|
|
|
return InvalidOffset();
|
|
|
|
|
|
|
|
// Offset is valid - compute it on unsigned.
|
|
|
|
int64_t WideIndex = static_cast<int64_t>(Index);
|
|
|
|
int64_t WideOffset = static_cast<int64_t>(Offset);
|
|
|
|
int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset);
|
|
|
|
S.Stk.push<Pointer>(Ptr.atIndex(static_cast<unsigned>(Result)));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool AddOffset(InterpState &S, CodePtr OpPC) {
|
|
|
|
return OffsetHelper<T, true>(S, OpPC);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool SubOffset(InterpState &S, CodePtr OpPC) {
|
|
|
|
return OffsetHelper<T, false>(S, OpPC);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Destroy
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
inline bool Destroy(InterpState &S, CodePtr OpPC, uint32_t I) {
|
|
|
|
S.Current->destroy(I);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Cast, CastFP
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) {
|
|
|
|
using T = typename PrimConv<TIn>::T;
|
|
|
|
using U = typename PrimConv<TOut>::T;
|
|
|
|
S.Stk.push<U>(U::from(S.Stk.pop<T>()));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Zero, Nullptr
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
bool Zero(InterpState &S, CodePtr OpPC) {
|
|
|
|
S.Stk.push<T>(T::zero());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType Name, class T = typename PrimConv<Name>::T>
|
|
|
|
inline bool Null(InterpState &S, CodePtr OpPC) {
|
|
|
|
S.Stk.push<T>();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// This, ImplicitThis
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
inline bool This(InterpState &S, CodePtr OpPC) {
|
|
|
|
// Cannot read 'this' in this mode.
|
|
|
|
if (S.checkingPotentialConstantExpression()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Pointer &This = S.Current->getThis();
|
|
|
|
if (!CheckThis(S, OpPC, This))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
S.Stk.push<Pointer>(This);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Shr, Shl
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <PrimType TR, PrimType TL, class T = typename PrimConv<TR>::T>
|
|
|
|
unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) {
|
|
|
|
// C++11 [expr.shift]p1: Shift width must be less than the bit width of
|
|
|
|
// the shifted type.
|
|
|
|
if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) {
|
|
|
|
const Expr *E = S.Current->getExpr(OpPC);
|
|
|
|
const APSInt Val = V.toAPSInt();
|
|
|
|
QualType Ty = E->getType();
|
|
|
|
S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
|
|
|
|
return Bits;
|
|
|
|
} else {
|
|
|
|
return static_cast<unsigned>(V);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
|
|
|
|
inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
|
|
|
|
if (RHS >= V.bitWidth()) {
|
|
|
|
S.Stk.push<T>(T::from(0, V.bitWidth()));
|
|
|
|
} else {
|
|
|
|
S.Stk.push<T>(T::from(V >> RHS, V.bitWidth()));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
|
|
|
|
inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
|
2020-04-21 15:37:19 -04:00
|
|
|
if (V.isSigned() && !S.getLangOpts().CPlusPlus20) {
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
// C++11 [expr.shift]p2: A signed left shift must have a non-negative
|
|
|
|
// operand, and must not overflow the corresponding unsigned type.
|
|
|
|
// C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to
|
|
|
|
// E1 x 2^E2 module 2^N.
|
|
|
|
if (V.isNegative()) {
|
|
|
|
const Expr *E = S.Current->getExpr(OpPC);
|
|
|
|
S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt();
|
|
|
|
} else if (V.countLeadingZeros() < RHS) {
|
|
|
|
S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (V.bitWidth() == 1) {
|
|
|
|
S.Stk.push<T>(V);
|
|
|
|
} else if (RHS >= V.bitWidth()) {
|
|
|
|
S.Stk.push<T>(T::from(0, V.bitWidth()));
|
|
|
|
} else {
|
|
|
|
S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth()));
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType TL, PrimType TR>
|
|
|
|
inline bool Shr(InterpState &S, CodePtr OpPC) {
|
|
|
|
const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
|
|
|
|
const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
|
|
|
|
const unsigned Bits = LHS.bitWidth();
|
|
|
|
|
|
|
|
if (RHS.isSigned() && RHS.isNegative()) {
|
|
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
|
|
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
|
|
|
|
return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
|
|
|
|
} else {
|
|
|
|
return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <PrimType TL, PrimType TR>
|
|
|
|
inline bool Shl(InterpState &S, CodePtr OpPC) {
|
|
|
|
const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
|
|
|
|
const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
|
|
|
|
const unsigned Bits = LHS.bitWidth();
|
|
|
|
|
|
|
|
if (RHS.isSigned() && RHS.isNegative()) {
|
|
|
|
const SourceInfo &Loc = S.Current->getSource(OpPC);
|
|
|
|
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
|
|
|
|
return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
|
|
|
|
} else {
|
|
|
|
return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// NoRet
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
inline bool NoRet(InterpState &S, CodePtr OpPC) {
|
|
|
|
SourceLocation EndLoc = S.Current->getCallee()->getEndLoc();
|
|
|
|
S.FFDiag(EndLoc, diag::note_constexpr_no_return);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// NarrowPtr, ExpandPtr
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
inline bool NarrowPtr(InterpState &S, CodePtr OpPC) {
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
S.Stk.push<Pointer>(Ptr.narrow());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool ExpandPtr(InterpState &S, CodePtr OpPC) {
|
|
|
|
const Pointer &Ptr = S.Stk.pop<Pointer>();
|
|
|
|
S.Stk.push<Pointer>(Ptr.expand());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
[Clang interpreter] Avoid storing pointers at unaligned locations
The Clang interpreter's bytecode uses a packed stream of bytes
representation, but also wants to have some opcodes take pointers as
arguments, which are currently embedded in the bytecode directly.
However, CHERI, and thus Arm's upcoming experimental Morello prototype,
provide spatial memory safety for C/C++ by implementing language-level
(and sub-language-level) pointers as capabilities, which track bounds,
permissions and validity in hardware. This uses tagged memory with a
single tag bit at every capability-aligned address, and so storing
pointers to unaligned addresses results in the tag being stripped,
leading to a tag fault when the pointer is ultimately dereferenced at a
later point.
In order to support a stricter C/C++ implementation like CHERI, we no
longer store pointers directly in the bytecode, instead storing them in
a table and embedding the index in the bytecode.
Reviewed By: nand
Differential Revision: https://reviews.llvm.org/D97606
2021-07-28 14:49:37 +01:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Read opcode arguments
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline std::enable_if_t<!std::is_pointer<T>::value, T> ReadArg(InterpState &S,
|
2022-08-05 13:57:39 +02:00
|
|
|
CodePtr &OpPC) {
|
[Clang interpreter] Avoid storing pointers at unaligned locations
The Clang interpreter's bytecode uses a packed stream of bytes
representation, but also wants to have some opcodes take pointers as
arguments, which are currently embedded in the bytecode directly.
However, CHERI, and thus Arm's upcoming experimental Morello prototype,
provide spatial memory safety for C/C++ by implementing language-level
(and sub-language-level) pointers as capabilities, which track bounds,
permissions and validity in hardware. This uses tagged memory with a
single tag bit at every capability-aligned address, and so storing
pointers to unaligned addresses results in the tag being stripped,
leading to a tag fault when the pointer is ultimately dereferenced at a
later point.
In order to support a stricter C/C++ implementation like CHERI, we no
longer store pointers directly in the bytecode, instead storing them in
a table and embedding the index in the bytecode.
Reviewed By: nand
Differential Revision: https://reviews.llvm.org/D97606
2021-07-28 14:49:37 +01:00
|
|
|
return OpPC.read<T>();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
inline std::enable_if_t<std::is_pointer<T>::value, T> ReadArg(InterpState &S,
|
2022-08-05 13:57:39 +02:00
|
|
|
CodePtr &OpPC) {
|
[Clang interpreter] Avoid storing pointers at unaligned locations
The Clang interpreter's bytecode uses a packed stream of bytes
representation, but also wants to have some opcodes take pointers as
arguments, which are currently embedded in the bytecode directly.
However, CHERI, and thus Arm's upcoming experimental Morello prototype,
provide spatial memory safety for C/C++ by implementing language-level
(and sub-language-level) pointers as capabilities, which track bounds,
permissions and validity in hardware. This uses tagged memory with a
single tag bit at every capability-aligned address, and so storing
pointers to unaligned addresses results in the tag being stripped,
leading to a tag fault when the pointer is ultimately dereferenced at a
later point.
In order to support a stricter C/C++ implementation like CHERI, we no
longer store pointers directly in the bytecode, instead storing them in
a table and embedding the index in the bytecode.
Reviewed By: nand
Differential Revision: https://reviews.llvm.org/D97606
2021-07-28 14:49:37 +01:00
|
|
|
uint32_t ID = OpPC.read<uint32_t>();
|
|
|
|
return reinterpret_cast<T>(S.P.getNativePointer(ID));
|
|
|
|
}
|
|
|
|
|
[Clang Interpreter] Initial patch for the constexpr interpreter
Summary:
This patch introduces the skeleton of the constexpr interpreter,
capable of evaluating a simple constexpr functions consisting of
if statements. The interpreter is described in more detail in the
RFC. Further patches will add more features.
Reviewers: Bigcheese, jfb, rsmith
Subscribers: bruno, uenoku, ldionne, Tyker, thegameg, tschuett, dexonsmith, mgorny, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64146
llvm-svn: 371834
2019-09-13 09:46:16 +00:00
|
|
|
/// Interpreter entry point.
|
|
|
|
bool Interpret(InterpState &S, APValue &Result);
|
|
|
|
|
|
|
|
} // namespace interp
|
|
|
|
} // namespace clang
|
|
|
|
|
|
|
|
#endif
|