Skip to content

[clang][Interp] Add IntegralAP for arbitrary-precision integers #65844

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,17 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->discard(SubExpr);
std::optional<PrimType> FromT = classify(SubExpr->getType());
std::optional<PrimType> ToT = classify(CE->getType());

if (!FromT || !ToT)
return false;

if (!this->visit(SubExpr))
return false;

if (FromT == ToT)
if (FromT == ToT) {
assert(ToT != PT_IntAP && ToT != PT_IntAPS);
return true;
}

return this->emitCast(*FromT, *ToT, CE);
}
Expand Down Expand Up @@ -1598,6 +1601,9 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(QualType QT,
return this->emitZeroSint64(E);
case PT_Uint64:
return this->emitZeroUint64(E);
case PT_IntAP:
case PT_IntAPS:
assert(false);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an llvm_unreachable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the assert(false) in this patch are just placeholders for things I need to implement in later patches. I'm trying to keep the patches smaller so they are easier to review.

case PT_Ptr:
return this->emitNullPtr(E);
case PT_FnPtr:
Expand Down Expand Up @@ -1837,6 +1843,9 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
return this->emitConstSint64(Value, E);
case PT_Uint64:
return this->emitConstUint64(Value, E);
case PT_IntAP:
case PT_IntAPS:
assert(false);
case PT_Bool:
return this->emitConstBool(Value, E);
case PT_Ptr:
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ std::optional<PrimType> Context::classify(QualType T) const {
case 8:
return PT_Sint8;
default:
return std::nullopt;
return PT_IntAPS;
}
}

Expand All @@ -118,7 +118,7 @@ std::optional<PrimType> Context::classify(QualType T) const {
case 8:
return PT_Uint8;
default:
return std::nullopt;
return PT_IntAP;
}
}

Expand Down
9 changes: 9 additions & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "Boolean.h"
#include "Floating.h"
#include "FunctionPointer.h"
#include "IntegralAP.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Record.h"
Expand Down Expand Up @@ -182,6 +183,10 @@ static BlockCtorFn getCtorPrim(PrimType Type) {
// constructor called.
if (Type == PT_Float)
return ctorTy<PrimConv<PT_Float>::T>;
if (Type == PT_IntAP)
return ctorTy<PrimConv<PT_IntAP>::T>;
if (Type == PT_IntAPS)
return ctorTy<PrimConv<PT_IntAPS>::T>;

COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr);
}
Expand All @@ -191,6 +196,10 @@ static BlockDtorFn getDtorPrim(PrimType Type) {
// destructor called, since they might allocate memory.
if (Type == PT_Float)
return dtorTy<PrimConv<PT_Float>::T>;
if (Type == PT_IntAP)
return dtorTy<PrimConv<PT_IntAP>::T>;
if (Type == PT_IntAPS)
return dtorTy<PrimConv<PT_IntAPS>::T>;

COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr);
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/EvalEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "EvalEmitter.h"
#include "ByteCodeGenError.h"
#include "Context.h"
#include "IntegralAP.h"
#include "Interp.h"
#include "Opcode.h"
#include "clang/AST/DeclCXX.h"
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/Interp/Integral.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ namespace interp {
using APInt = llvm::APInt;
using APSInt = llvm::APSInt;

template <bool Signed> class IntegralAP;

// Helper structure to select the representation.
template <unsigned Bits, bool Signed> struct Repr;
template <> struct Repr<8, false> { using Type = uint8_t; };
Expand Down Expand Up @@ -61,6 +63,8 @@ template <unsigned Bits, bool Signed> class Integral final {
template <typename T> explicit Integral(T V) : V(V) {}

public:
using AsUnsigned = Integral<Bits, false>;

/// Zero-initializes an integral.
Integral() : V(0) {}

Expand Down
256 changes: 256 additions & 0 deletions clang/lib/AST/Interp/IntegralAP.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
//===--- Integral.h - Wrapper for numeric types for the 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
//
//===----------------------------------------------------------------------===//
//
// Defines the VM types and helpers operating on types.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_INTERP_INTEGRAL_AP_H
#define LLVM_CLANG_AST_INTERP_INTEGRAL_AP_H

#include "clang/AST/APValue.h"
#include "clang/AST/ComparisonCategories.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/raw_ostream.h"
#include <cstddef>
#include <cstdint>

#include "Primitives.h"

namespace clang {
namespace interp {

using APInt = llvm::APInt;
using APSInt = llvm::APSInt;
template <unsigned Bits, bool Signed> class Integral;
class Boolean;

template <bool Signed> class IntegralAP final {
public:
APSInt V;

public:
using AsUnsigned = IntegralAP<false>;

template <typename T>
IntegralAP(T Value) : V(APInt(sizeof(T) * 8, Value, std::is_signed_v<T>)) {}

IntegralAP(APInt V) : V(V) {}
IntegralAP(APSInt V) : V(V) {}
/// Arbitrary value for initialized variables.
IntegralAP() : V(APSInt::getMaxValue(1024, Signed)) {}

IntegralAP operator-() const { return IntegralAP(-V); }
bool operator>(IntegralAP RHS) const { return V > RHS.V; }
bool operator>=(IntegralAP RHS) const { return V >= RHS.V; }
bool operator<(IntegralAP RHS) const { return V < RHS.V; }
bool operator<=(IntegralAP RHS) const { return V <= RHS.V; }

explicit operator bool() const { return !V.isZero(); }
explicit operator int8_t() const { return V.getSExtValue(); }
explicit operator uint8_t() const { return V.getZExtValue(); }
explicit operator int16_t() const { return V.getSExtValue(); }
explicit operator uint16_t() const { return V.getZExtValue(); }
explicit operator int32_t() const { return V.getSExtValue(); }
explicit operator uint32_t() const { return V.getZExtValue(); }
explicit operator int64_t() const { return V.getSExtValue(); }
explicit operator uint64_t() const { return V.getZExtValue(); }

template <typename T> static IntegralAP from(T Value, unsigned NumBits = 0) {
assert(NumBits > 0);
APSInt Copy = APSInt(APInt(NumBits, Value, Signed), !Signed);

return IntegralAP<Signed>(Copy);
}

template <bool InputSigned>
static IntegralAP from(IntegralAP<InputSigned> V, unsigned NumBits = 0) {
if constexpr (Signed == InputSigned)
return V;

APSInt Copy = V.V;
Copy.setIsSigned(Signed);

return IntegralAP<Signed>(Copy);
}

template <unsigned Bits, bool InputSigned>
static IntegralAP from(Integral<Bits, InputSigned> I) {
// FIXME: Take bits parameter.
APSInt Copy =
APSInt(APInt(128, static_cast<int64_t>(I), InputSigned), !Signed);
Copy.setIsSigned(Signed);

assert(Copy.isSigned() == Signed);
return IntegralAP<Signed>(Copy);
}
static IntegralAP from(const Boolean &B) {
assert(false);
return IntegralAP::zero();
}

static IntegralAP zero() {
assert(false);
return IntegralAP(0);
}

// FIXME: This can't be static if the bitwidth depends on V.
static constexpr unsigned bitWidth() { return 128; }

APSInt toAPSInt(unsigned Bits = 0) const { return V; }
APValue toAPValue() const { return APValue(V); }

bool isZero() const { return V.isZero(); }
bool isPositive() const { return V.isNonNegative(); }
bool isNegative() const { return !V.isNonNegative(); }
bool isMin() const { return V.isMinValue(); }
bool isMax() const { return V.isMaxValue(); }
static bool isSigned() { return Signed; }
bool isMinusOne() const { return Signed && V == -1; }

unsigned countLeadingZeros() const { return V.countl_zero(); }

void print(llvm::raw_ostream &OS) const { OS << V; }

IntegralAP truncate(unsigned bitWidth) const {
assert(false);
return V;
}

IntegralAP<false> toUnsigned() const {
APSInt Copy = V;
Copy.setIsSigned(false);
return IntegralAP<false>(Copy);
}

ComparisonCategoryResult compare(const IntegralAP &RHS) const {
return Compare(V, RHS.V);
}

static bool increment(IntegralAP A, IntegralAP *R) {
assert(false);
*R = IntegralAP(A.V + 1);
return false;
}

static bool decrement(IntegralAP A, IntegralAP *R) {
assert(false);
*R = IntegralAP(A.V - 1);
return false;
}

static bool add(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
return CheckAddUB(A, B, OpBits, R);
}

static bool sub(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
/// FIXME: Gotta check if the result fits into OpBits bits.
return CheckSubUB(A, B, R);
}

static bool mul(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
assert(false);
// return CheckMulUB(A.V, B.V, R->V);
return false;
}

static bool rem(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
assert(false);
*R = IntegralAP(A.V % B.V);
return false;
}

static bool div(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
assert(false);
*R = IntegralAP(A.V / B.V);
return false;
}

static bool bitAnd(IntegralAP A, IntegralAP B, unsigned OpBits,
IntegralAP *R) {
assert(false);
*R = IntegralAP(A.V & B.V);
return false;
}

static bool bitOr(IntegralAP A, IntegralAP B, unsigned OpBits,
IntegralAP *R) {
assert(false);
*R = IntegralAP(A.V | B.V);
return false;
}

static bool bitXor(IntegralAP A, IntegralAP B, unsigned OpBits,
IntegralAP *R) {
assert(false);
*R = IntegralAP(A.V ^ B.V);
return false;
}

static bool neg(const IntegralAP &A, IntegralAP *R) {
APSInt AI = A.V;

AI.setIsSigned(Signed);
*R = IntegralAP(AI);
return false;
}

static bool comp(IntegralAP A, IntegralAP *R) {
assert(false);
*R = IntegralAP(~A.V);
return false;
}

static void shiftLeft(const IntegralAP A, const IntegralAP B, unsigned OpBits,
IntegralAP *R) {
*R = IntegralAP(A.V << B.V.getZExtValue());
}

static void shiftRight(const IntegralAP A, const IntegralAP B,
unsigned OpBits, IntegralAP *R) {
*R = IntegralAP(A.V >> B.V.getZExtValue());
}

private:
static bool CheckAddUB(const IntegralAP &A, const IntegralAP &B,
unsigned BitWidth, IntegralAP *R) {
if (!A.isSigned()) {
R->V = A.V + B.V;
return false;
}

const APSInt &LHS = A.V;
const APSInt &RHS = B.V;

APSInt Value(LHS.extend(BitWidth) + RHS.extend(BitWidth), false);
APSInt Result = Value.trunc(LHS.getBitWidth());
if (Result.extend(BitWidth) != Value)
return true;

R->V = Result;
return false;
}
static bool CheckSubUB(const IntegralAP &A, const IntegralAP &B,
IntegralAP *R) {
R->V = A.V - B.V;
return false; // Success!
}
};

template <bool Signed>
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
IntegralAP<Signed> I) {
I.print(OS);
return OS;
}

} // namespace interp
} // namespace clang

#endif
Loading