Skip to content

Commit

Permalink
Re-apply "[clang][Interp] Support floating-point values"
Browse files Browse the repository at this point in the history
Don't move the Repr struct into Integral this time.

Differential Revision: https://reviews.llvm.org/D134859
  • Loading branch information
tbaederr committed Jan 25, 2023
1 parent be4b873 commit cb7f582
Show file tree
Hide file tree
Showing 22 changed files with 620 additions and 39 deletions.
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ add_clang_library(clangAST
Interp/Frame.cpp
Interp/Function.cpp
Interp/InterpBuiltin.cpp
Interp/Floating.cpp
Interp/Interp.cpp
Interp/InterpBlock.cpp
Interp/InterpFrame.cpp
Expand Down
4 changes: 1 addition & 3 deletions clang/lib/AST/Interp/Boolean.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ class Boolean final {
/// Underlying boolean.
bool V;

/// Construct a wrapper from a boolean.
explicit Boolean(bool V) : V(V) {}

public:
/// Zero-initializes a boolean.
Boolean() : V(false) {}
explicit Boolean(bool V) : V(V) {}

bool operator<(Boolean RHS) const { return V < RHS.V; }
bool operator>(Boolean RHS) const { return V > RHS.V; }
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "ByteCodeEmitter.h"
#include "Context.h"
#include "Floating.h"
#include "Opcode.h"
#include "Program.h"
#include "clang/AST/DeclCXX.h"
Expand Down
58 changes: 58 additions & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "ByteCodeGenError.h"
#include "ByteCodeStmtGen.h"
#include "Context.h"
#include "Floating.h"
#include "Function.h"
#include "PrimType.h"
#include "Program.h"
Expand Down Expand Up @@ -95,6 +96,41 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
return this->emitGetPtrBase(ToBase->Offset, CE);
}

case CK_FloatingCast: {
if (!this->visit(SubExpr))
return false;
const auto *TargetSemantics =
&Ctx.getASTContext().getFloatTypeSemantics(CE->getType());
return this->emitCastFP(TargetSemantics, getRoundingMode(CE), CE);
}

case CK_IntegralToFloating: {
std::optional<PrimType> FromT = classify(SubExpr->getType());
if (!FromT)
return false;

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

const auto *TargetSemantics =
&Ctx.getASTContext().getFloatTypeSemantics(CE->getType());
llvm::RoundingMode RM = getRoundingMode(CE);
return this->emitCastIntegralFloating(*FromT, TargetSemantics, RM, CE);
}

case CK_FloatingToBoolean:
case CK_FloatingToIntegral: {
std::optional<PrimType> ToT = classify(CE->getType());

if (!ToT)
return false;

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

return this->emitCastFloatingIntegral(*ToT, CE);
}

case CK_ArrayToPointerDecay:
case CK_AtomicToNonAtomic:
case CK_ConstructorConversion:
Expand Down Expand Up @@ -136,6 +172,14 @@ bool ByteCodeExprGen<Emitter>::VisitIntegerLiteral(const IntegerLiteral *LE) {
return this->emitConst(LE->getValue(), LE);
}

template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitFloatingLiteral(const FloatingLiteral *E) {
if (DiscardResult)
return true;

return this->emitConstFloat(E->getValue(), E);
}

template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) {
return this->visit(PE->getSubExpr());
Expand Down Expand Up @@ -195,14 +239,22 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
case BO_GE:
return Discard(this->emitGE(*LT, BO));
case BO_Sub:
if (BO->getType()->isFloatingType())
return Discard(this->emitSubf(getRoundingMode(BO), BO));
return Discard(this->emitSub(*T, BO));
case BO_Add:
if (BO->getType()->isFloatingType())
return Discard(this->emitAddf(getRoundingMode(BO), BO));
return Discard(this->emitAdd(*T, BO));
case BO_Mul:
if (BO->getType()->isFloatingType())
return Discard(this->emitMulf(getRoundingMode(BO), BO));
return Discard(this->emitMul(*T, BO));
case BO_Rem:
return Discard(this->emitRem(*T, BO));
case BO_Div:
if (BO->getType()->isFloatingType())
return Discard(this->emitDivf(getRoundingMode(BO), BO));
return Discard(this->emitDiv(*T, BO));
case BO_Assign:
if (DiscardResult)
Expand Down Expand Up @@ -515,6 +567,9 @@ bool ByteCodeExprGen<Emitter>::VisitCompoundAssignOperator(
assert(!E->getType()->isPointerType() &&
"Support pointer arithmethic in compound assignment operators");

assert(!E->getType()->isFloatingType() &&
"Support floating types in compound assignment operators");

// Get LHS pointer, load its value and get RHS value.
if (!visit(LHS))
return false;
Expand Down Expand Up @@ -636,6 +691,8 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, const Expr *E) {
return this->emitZeroUint64(E);
case PT_Ptr:
return this->emitNullPtr(E);
case PT_Float:
assert(false);
}
llvm_unreachable("unknown primitive type");
}
Expand Down Expand Up @@ -800,6 +857,7 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, const Expr *E) {
case PT_Bool:
return this->emitConstBool(Value, E);
case PT_Ptr:
case PT_Float:
llvm_unreachable("Invalid integral type");
break;
}
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
// Expression visitors - result returned on interp stack.
bool VisitCastExpr(const CastExpr *E);
bool VisitIntegerLiteral(const IntegerLiteral *E);
bool VisitFloatingLiteral(const FloatingLiteral *E);
bool VisitParenExpr(const ParenExpr *E);
bool VisitBinaryOperator(const BinaryOperator *E);
bool VisitPointerArithBinOp(const BinaryOperator *E);
Expand Down Expand Up @@ -234,6 +235,15 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
return VD->hasGlobalStorage() || VD->isConstexpr();
}

llvm::RoundingMode getRoundingMode(const Expr *E) const {
FPOptions FPO = E->getFPFeaturesInEffect(Ctx.getLangOpts());

if (FPO.getRoundingMode() == llvm::RoundingMode::Dynamic)
return llvm::RoundingMode::NearestTiesToEven;

return FPO.getRoundingMode();
}

protected:
/// Variable to storage mapping.
llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ std::optional<PrimType> Context::classify(QualType T) const {
if (T->isNullPtrType())
return PT_Ptr;

if (T->isFloatingType())
return PT_Float;

if (auto *AT = dyn_cast<AtomicType>(T))
return classify(AT->getValueType());

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/AST/Interp/Descriptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "Descriptor.h"
#include "Boolean.h"
#include "Floating.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Record.h"
Expand Down Expand Up @@ -170,6 +171,11 @@ static void moveRecord(Block *B, char *Src, char *Dst, Descriptor *D) {
}

static BlockCtorFn getCtorPrim(PrimType Type) {
// Floating types are special. They are primitives, but need their
// constructor called.
if (Type == PT_Float)
return ctorTy<PrimConv<PT_Float>::T>;

COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr);
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

#include "Floating.h"
#include "Function.h"
#include "Opcode.h"
#include "PrimType.h"
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/AST/Interp/Floating.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//===---- Floating.cpp - Support for floating point values ------*- 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
//
//===----------------------------------------------------------------------===//

#include "Floating.h"

namespace clang {
namespace interp {

llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F) {
F.print(OS);
return OS;
}

Floating getSwappedBytes(Floating F) { return F; }

} // namespace interp
} // namespace clang
144 changes: 144 additions & 0 deletions clang/lib/AST/Interp/Floating.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//===--- Floating.h - Types 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
//
//===----------------------------------------------------------------------===//
//
// Defines the VM types and helpers operating on types.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_INTERP_FLOATING_H
#define LLVM_CLANG_AST_INTERP_FLOATING_H

#include "Primitives.h"
#include "clang/AST/APValue.h"
#include "llvm/ADT/APFloat.h"

namespace clang {
namespace interp {

using APFloat = llvm::APFloat;
using APSInt = llvm::APSInt;

class Floating final {
private:
// The underlying value storage.
APFloat F;

public:
/// Zero-initializes a Floating.
Floating() : F(0.0f) {}
Floating(const APFloat &F) : F(F) {}

// Static constructors for special floating point values.
static Floating getInf(const llvm::fltSemantics &Sem) {
return Floating(APFloat::getInf(Sem));
}
const APFloat &getAPFloat() const { return F; }

bool operator<(Floating RHS) const { return F < RHS.F; }
bool operator>(Floating RHS) const { return F > RHS.F; }
bool operator<=(Floating RHS) const { return F <= RHS.F; }
bool operator>=(Floating RHS) const { return F >= RHS.F; }
bool operator==(Floating RHS) const { return F == RHS.F; }
bool operator!=(Floating RHS) const { return F != RHS.F; }
Floating operator-() const { return Floating(-F); }

APFloat::opStatus convertToInteger(APSInt &Result) const {
bool IsExact;
return F.convertToInteger(Result, llvm::APFloat::rmTowardZero, &IsExact);
}

Floating toSemantics(const llvm::fltSemantics *Sem,
llvm::RoundingMode RM) const {
APFloat Copy = F;
bool LosesInfo;
Copy.convert(*Sem, RM, &LosesInfo);
(void)LosesInfo;
return Floating(Copy);
}

/// Convert this Floating to one with the same semantics as \Other.
Floating toSemantics(const Floating &Other, llvm::RoundingMode RM) const {
return toSemantics(&Other.F.getSemantics(), RM);
}

APSInt toAPSInt(unsigned NumBits = 0) const {
return APSInt(F.bitcastToAPInt());
}
APValue toAPValue() const { return APValue(F); }
void print(llvm::raw_ostream &OS) const {
// Can't use APFloat::print() since it appends a newline.
SmallVector<char, 16> Buffer;
F.toString(Buffer);
OS << Buffer;
}

unsigned bitWidth() const { return F.semanticsSizeInBits(F.getSemantics()); }

bool isSigned() const { return true; }
bool isNegative() const { return F.isNegative(); }
bool isPositive() const { return !F.isNegative(); }
bool isZero() const { return F.isZero(); }
bool isNonZero() const { return F.isNonZero(); }
bool isMin() const { return F.isSmallest(); }
bool isMinusOne() const { return F.isExactlyValue(-1.0); }
bool isNan() const { return F.isNaN(); }
bool isFinite() const { return F.isFinite(); }

ComparisonCategoryResult compare(const Floating &RHS) const {
return Compare(F, RHS.F);
}

static APFloat::opStatus fromIntegral(APSInt Val,
const llvm::fltSemantics &Sem,
llvm::RoundingMode RM,
Floating &Result) {
APFloat F = APFloat(Sem);
APFloat::opStatus Status = F.convertFromAPInt(Val, Val.isSigned(), RM);
Result = Floating(F);
return Status;
}

// -------

static APFloat::opStatus add(Floating A, Floating B, llvm::RoundingMode RM,
Floating *R) {
*R = Floating(A.F);
return R->F.add(B.F, RM);
}

static APFloat::opStatus sub(Floating A, Floating B, llvm::RoundingMode RM,
Floating *R) {
*R = Floating(A.F);
return R->F.subtract(B.F, RM);
}

static APFloat::opStatus mul(Floating A, Floating B, llvm::RoundingMode RM,
Floating *R) {
*R = Floating(A.F);
return R->F.multiply(B.F, RM);
}

static APFloat::opStatus div(Floating A, Floating B, llvm::RoundingMode RM,
Floating *R) {
*R = Floating(A.F);
return R->F.divide(B.F, RM);
}

static bool neg(Floating A, Floating *R) {
*R = -A;
return false;
}
};

llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Floating F);
Floating getSwappedBytes(Floating F);

} // namespace interp
} // namespace clang

#endif
12 changes: 2 additions & 10 deletions clang/lib/AST/Interp/Integral.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,14 @@
#include <cstddef>
#include <cstdint>

#include "Primitives.h"

namespace clang {
namespace interp {

using APInt = llvm::APInt;
using APSInt = llvm::APSInt;

/// Helper to compare two comparable types.
template <typename T>
ComparisonCategoryResult Compare(const T &X, const T &Y) {
if (X < Y)
return ComparisonCategoryResult::Less;
if (X > Y)
return ComparisonCategoryResult::Greater;
return ComparisonCategoryResult::Equal;
}

// Helper structure to select the representation.
template <unsigned Bits, bool Signed> struct Repr;
template <> struct Repr<8, false> { using Type = uint8_t; };
Expand Down
Loading

0 comments on commit cb7f582

Please sign in to comment.