Skip to content

[clang][bytecode] Implement fixed-point-to-int casts #110417

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 1 commit into from
Sep 29, 2024
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
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Boolean.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Boolean final {
public:
/// Zero-initializes a boolean.
Boolean() : V(false) {}
Boolean(const llvm::APSInt &I) : V(!I.isZero()) {}
explicit Boolean(bool V) : V(V) {}

bool operator<(Boolean RHS) const { return V < RHS.V; }
Expand Down
28 changes: 27 additions & 1 deletion clang/lib/AST/ByteCode/Compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,11 @@ bool Compiler<Emitter>::VisitCastExpr(const CastExpr *CE) {
const auto *TargetSemantics = &Ctx.getFloatSemantics(CE->getType());
return this->emitCastFixedPointFloating(TargetSemantics, CE);
}
case CK_FixedPointToIntegral: {
if (!this->visit(SubExpr))
return false;
return this->emitCastFixedPointIntegral(classifyPrim(CE->getType()), CE);
}
case CK_FixedPointCast: {
if (!this->visit(SubExpr))
return false;
Expand Down Expand Up @@ -1562,6 +1567,25 @@ bool Compiler<Emitter>::VisitFixedPointBinOp(const BinaryOperator *E) {
llvm_unreachable("unhandled binop opcode");
}

template <class Emitter>
bool Compiler<Emitter>::VisitFixedPointUnaryOperator(const UnaryOperator *E) {
const Expr *SubExpr = E->getSubExpr();
assert(SubExpr->getType()->isFixedPointType());

switch (E->getOpcode()) {
case UO_Plus:
return this->delegate(SubExpr);
case UO_Minus:
if (!this->visit(SubExpr))
return false;
return this->emitNegFixedPoint(E);
default:
return false;
}

llvm_unreachable("Unhandled unary opcode");
}

template <class Emitter>
bool Compiler<Emitter>::VisitImplicitValueInitExpr(
const ImplicitValueInitExpr *E) {
Expand Down Expand Up @@ -3805,7 +3829,7 @@ bool Compiler<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
case PT_FixedPoint: {
auto Sem = Ctx.getASTContext().getFixedPointSemantics(E->getType());
return this->emitConstFixedPoint(FixedPoint::Zero(Sem), E);
return this->emitConstFixedPoint(FixedPoint::zero(Sem), E);
}
llvm_unreachable("Implement");
}
Expand Down Expand Up @@ -5471,6 +5495,8 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
return this->VisitComplexUnaryOperator(E);
if (SubExpr->getType()->isVectorType())
return this->VisitVectorUnaryOperator(E);
if (SubExpr->getType()->isFixedPointType())
return this->VisitFixedPointUnaryOperator(E);
std::optional<PrimType> T = classify(SubExpr->getType());

switch (E->getOpcode()) {
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/ByteCode/Compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool VisitComplexBinOp(const BinaryOperator *E);
bool VisitVectorBinOp(const BinaryOperator *E);
bool VisitFixedPointBinOp(const BinaryOperator *E);
bool VisitFixedPointUnaryOperator(const UnaryOperator *E);
bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E);
bool VisitCallExpr(const CallExpr *E);
bool VisitBuiltinCallExpr(const CallExpr *E, unsigned BuiltinID);
Expand Down
19 changes: 13 additions & 6 deletions clang/lib/AST/ByteCode/FixedPoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,20 @@ class FixedPoint final {
: V(APInt(0, 0ULL, false),
llvm::FixedPointSemantics(0, 0, false, false, false)) {}

static FixedPoint Zero(llvm::FixedPointSemantics Sem) {
static FixedPoint zero(llvm::FixedPointSemantics Sem) {
return FixedPoint(APInt(Sem.getWidth(), 0ULL, Sem.isSigned()), Sem);
}

operator bool() const { return V.getBoolValue(); }
template <typename Ty, typename = std::enable_if_t<std::is_integral_v<Ty>>>
explicit operator Ty() const {
// FIXME
return 0;
static FixedPoint from(const APSInt &I, llvm::FixedPointSemantics Sem,
bool *Overflow) {
return FixedPoint(llvm::APFixedPoint::getFromIntValue(I, Sem, Overflow));
}
static FixedPoint from(const llvm::APFloat &I, llvm::FixedPointSemantics Sem,
bool *Overflow) {
return FixedPoint(llvm::APFixedPoint::getFromFloatValue(I, Sem, Overflow));
}

operator bool() const { return V.getBoolValue(); }
void print(llvm::raw_ostream &OS) const { OS << V; }

APValue toAPValue(const ASTContext &) const { return APValue(V); }
Expand All @@ -70,6 +73,10 @@ class FixedPoint final {
return V.convertToFloat(*Sem);
}

llvm::APSInt toInt(unsigned BitWidth, bool Signed, bool *Overflow) const {
return V.convertToInt(BitWidth, Signed, Overflow);
}

std::string toDiagnosticString(const ASTContext &Ctx) const {
return V.toString();
}
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/AST/ByteCode/Interp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1393,6 +1393,19 @@ bool InvalidNewDeleteExpr(InterpState &S, CodePtr OpPC, const Expr *E) {
return false;
}

bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
const FixedPoint &FP) {
const Expr *E = S.Current->getExpr(OpPC);
if (S.checkingForUndefinedBehavior()) {
S.getASTContext().getDiagnostics().Report(
E->getExprLoc(), diag::warn_fixedpoint_constant_overflow)
<< FP.toDiagnosticString(S.getASTContext()) << E->getType();
}
S.CCEDiag(E, diag::note_constexpr_overflow)
<< FP.toDiagnosticString(S.getASTContext()) << E->getType();
return S.noteUndefinedBehavior();
}

bool Interpret(InterpState &S, APValue &Result) {
// The current stack frame when we started Interpret().
// This is being used by the ops to determine wheter
Expand Down
98 changes: 38 additions & 60 deletions clang/lib/AST/ByteCode/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ bool CallPtr(InterpState &S, CodePtr OpPC, uint32_t ArgSize,
const CallExpr *CE);
bool CheckLiteralType(InterpState &S, CodePtr OpPC, const Type *T);

template <typename T>
static bool handleOverflow(InterpState &S, CodePtr OpPC, const T &SrcValue) {
const Expr *E = S.Current->getExpr(OpPC);
S.CCEDiag(E, diag::note_constexpr_overflow) << SrcValue << E->getType();
return S.noteUndefinedBehavior();
}
bool handleFixedPointOverflow(InterpState &S, CodePtr OpPC,
const FixedPoint &FP);

enum class ShiftDir { Left, Right };

/// Checks if the shift operation is legal.
Expand Down Expand Up @@ -385,13 +394,10 @@ bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS,
<< Trunc << Type << E->getSourceRange();
}

S.CCEDiag(E, diag::note_constexpr_overflow) << Value << Type;

if (!S.noteUndefinedBehavior()) {
if (!handleOverflow(S, OpPC, Value)) {
S.Stk.pop<T>();
return false;
}

return true;
}

Expand Down Expand Up @@ -741,8 +747,7 @@ bool Neg(InterpState &S, CodePtr OpPC) {
return true;
}

S.CCEDiag(E, diag::note_constexpr_overflow) << NegatedValue << Type;
return S.noteUndefinedBehavior();
return handleOverflow(S, OpPC, NegatedValue);
}

enum class PushVal : bool {
Expand Down Expand Up @@ -804,8 +809,7 @@ bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return true;
}

S.CCEDiag(E, diag::note_constexpr_overflow) << APResult << Type;
return S.noteUndefinedBehavior();
return handleOverflow(S, OpPC, APResult);
}

/// 1) Pops a pointer from the stack
Expand Down Expand Up @@ -2170,18 +2174,8 @@ inline bool CastFixedPoint(InterpState &S, CodePtr OpPC, uint32_t FPS) {
bool Overflow;
FixedPoint Result = Source.toSemantics(TargetSemantics, &Overflow);

if (Overflow) {
const Expr *E = S.Current->getExpr(OpPC);
if (S.checkingForUndefinedBehavior()) {
S.getASTContext().getDiagnostics().Report(
E->getExprLoc(), diag::warn_fixedpoint_constant_overflow)
<< Result.toDiagnosticString(S.getASTContext()) << E->getType();
}
S.CCEDiag(E, diag::note_constexpr_overflow)
<< Result.toDiagnosticString(S.getASTContext()) << E->getType();
if (!S.noteUndefinedBehavior())
return false;
}
if (Overflow && !handleFixedPointOverflow(S, OpPC, Result))
return false;

S.Stk.push<FixedPoint>(Result);
return true;
Expand Down Expand Up @@ -2257,13 +2251,8 @@ static inline bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC,
auto Status = F.convertToInteger(Result);

// Float-to-Integral overflow check.
if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
const Expr *E = S.Current->getExpr(OpPC);
QualType Type = E->getType();

S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
return S.noteUndefinedBehavior();
}
if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite())
return handleOverflow(S, OpPC, F.getAPFloat());

FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
Expand All @@ -2278,13 +2267,8 @@ static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC,
auto Status = F.convertToInteger(Result);

// Float-to-Integral overflow check.
if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
const Expr *E = S.Current->getExpr(OpPC);
QualType Type = E->getType();

S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
return S.noteUndefinedBehavior();
}
if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite())
return handleOverflow(S, OpPC, F.getAPFloat());

FPOptions FPO = FPOptions::getFromOpaqueInt(FPOI);
S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
Expand Down Expand Up @@ -2347,20 +2331,10 @@ static inline bool CastIntegralFixedPoint(InterpState &S, CodePtr OpPC,
std::memcpy(&Sem, &FPS, sizeof(Sem));

bool Overflow;
llvm::APFixedPoint Result =
llvm::APFixedPoint::getFromIntValue(Int.toAPSInt(), Sem, &Overflow);
FixedPoint Result = FixedPoint::from(Int.toAPSInt(), Sem, &Overflow);

if (Overflow) {
const Expr *E = S.Current->getExpr(OpPC);
if (S.checkingForUndefinedBehavior()) {
S.getASTContext().getDiagnostics().Report(
E->getExprLoc(), diag::warn_fixedpoint_constant_overflow)
<< Result.toString() << E->getType();
}
S.CCEDiag(E, diag::note_constexpr_overflow) << Result << E->getType();
if (!S.noteUndefinedBehavior())
return false;
}
if (Overflow && !handleFixedPointOverflow(S, OpPC, Result))
return false;

S.Stk.push<FixedPoint>(Result);
return true;
Expand All @@ -2374,20 +2348,10 @@ static inline bool CastFloatingFixedPoint(InterpState &S, CodePtr OpPC,
std::memcpy(&Sem, &FPS, sizeof(Sem));

bool Overflow;
llvm::APFixedPoint Result =
llvm::APFixedPoint::getFromFloatValue(Float.getAPFloat(), Sem, &Overflow);
FixedPoint Result = FixedPoint::from(Float.getAPFloat(), Sem, &Overflow);

if (Overflow) {
const Expr *E = S.Current->getExpr(OpPC);
if (S.checkingForUndefinedBehavior()) {
S.getASTContext().getDiagnostics().Report(
E->getExprLoc(), diag::warn_fixedpoint_constant_overflow)
<< Result.toString() << E->getType();
}
S.CCEDiag(E, diag::note_constexpr_overflow) << Result << E->getType();
if (!S.noteUndefinedBehavior())
return false;
}
if (Overflow && !handleFixedPointOverflow(S, OpPC, Result))
return false;

S.Stk.push<FixedPoint>(Result);
return true;
Expand All @@ -2401,6 +2365,20 @@ static inline bool CastFixedPointFloating(InterpState &S, CodePtr OpPC,
return true;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
static inline bool CastFixedPointIntegral(InterpState &S, CodePtr OpPC) {
const auto &Fixed = S.Stk.pop<FixedPoint>();

bool Overflow;
APSInt Int = Fixed.toInt(T::bitWidth(), T::isSigned(), &Overflow);

if (Overflow && !handleOverflow(S, OpPC, Int))
return false;

S.Stk.push<T>(Int);
return true;
}

static inline bool PtrPtrCast(InterpState &S, CodePtr OpPC, bool SrcIsVoidPtr) {
const auto &Ptr = S.Stk.peek<Pointer>();

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/ByteCode/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,10 @@ def CastFloatingFixedPoint : Opcode {
def CastFixedPointFloating : Opcode {
let Args = [ArgFltSemantics];
}
def CastFixedPointIntegral : Opcode {
let Types = [FixedSizeIntegralTypes];
let HasGroup = 1;
}

def PtrPtrCast : Opcode {
let Args = [ArgBool];
Expand Down
6 changes: 6 additions & 0 deletions clang/test/AST/ByteCode/fixed-point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ namespace IntToFixedPointCast {
static_assert(sf == -1);
}

namespace FixedPointToIntCasts {
constexpr _Accum A = -13.0k;
constexpr int I = A;
static_assert(I == -13);
}

namespace FloatToFixedPointCast {
constexpr _Fract sf = 1.0; // both-error {{must be initialized by a constant expression}} \
// both-note {{outside the range of representable values of type 'const _Fract'}}
Expand Down
3 changes: 3 additions & 0 deletions clang/test/Frontend/fixed_point_conversions_const.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,SIGNED
// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point | FileCheck %s --check-prefixes=CHECK,UNSIGNED

// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK,SIGNED
// RUN: %clang_cc1 -ffixed-point -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - -fpadding-on-unsigned-fixed-point -fexperimental-new-constant-interpreter | FileCheck %s --check-prefixes=CHECK,UNSIGNED

// Between different fixed point types
short _Accum sa_const = 2.5hk;
// CHECK-DAG: @sa_const = {{.*}}global i16 320, align 2
Expand Down
Loading