Skip to content

Commit

Permalink
[clang][Interp] Add IntegralAP for arbitrary-precision integers (#65844)
Browse files Browse the repository at this point in the history
This adds `IntegralAP` backing the two new primtypes `IntAP` (unsigned
arbitrary-precision int) and `IntAPS` (same but signed).

We use this for `int128` support (which isn't available on all host
systems we support AFAIK) and I think we can also use this for `_BitInt`
later.
  • Loading branch information
tbaederr authored and pull[bot] committed Feb 16, 2024
1 parent 7516a2b commit 4837592
Show file tree
Hide file tree
Showing 12 changed files with 383 additions and 14 deletions.
11 changes: 10 additions & 1 deletion clang/lib/AST/Interp/ByteCodeExprGen.cpp
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 @@ -1638,6 +1641,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);
case PT_Ptr:
return this->emitNullPtr(E);
case PT_FnPtr:
Expand Down Expand Up @@ -1877,6 +1883,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
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
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
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
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
@@ -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

0 comments on commit 4837592

Please sign in to comment.