Skip to content
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

[SPIR-V] Move constant evaluation code into a new class #6195

Merged
merged 2 commits into from
Jan 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 tools/clang/lib/SPIRV/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ add_clang_library(clangSPIRV
AstTypeProbe.cpp
BlockReadableOrder.cpp
CapabilityVisitor.cpp
ConstEvaluator.cpp
DeclResultIdMapper.cpp
DebugTypeVisitor.cpp
EmitSpirvAction.cpp
Expand Down
154 changes: 154 additions & 0 deletions tools/clang/lib/SPIRV/ConstEvaluator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
//===------- ConstEvaluator.cpp ----- Translate Constants -------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//
//
// This file implements methods for translating AST expressions to SPIR-V
// constants.
//
//===----------------------------------------------------------------------===//

#include "ConstEvaluator.h"

namespace clang {
namespace spirv {

/// Returns true iff the given expression is a literal integer that cannot be
/// represented in a 32-bit integer type or a literal float that cannot be
/// represented in a 32-bit float type without losing info. Returns false
/// otherwise.
bool isLiteralLargerThan32Bits(const Expr *expr) {
if (const auto *intLiteral = dyn_cast<IntegerLiteral>(expr)) {
const bool isSigned = expr->getType()->isSignedIntegerType();
const llvm::APInt &value = intLiteral->getValue();
return (isSigned && !value.isSignedIntN(32)) ||
(!isSigned && !value.isIntN(32));
}

if (const auto *floatLiteral = dyn_cast<FloatingLiteral>(expr)) {
llvm::APFloat value = floatLiteral->getValue();
const auto &semantics = value.getSemantics();
// regular 'half' and 'float' can be represented in 32 bits.
if (&semantics == &llvm::APFloat::IEEEsingle ||
&semantics == &llvm::APFloat::IEEEhalf)
return true;

// See if 'double' value can be represented in 32 bits without losing info.
bool losesInfo = false;
const auto convertStatus =
value.convert(llvm::APFloat::IEEEsingle,
llvm::APFloat::rmNearestTiesToEven, &losesInfo);
if (convertStatus != llvm::APFloat::opOK &&
convertStatus != llvm::APFloat::opInexact)
return true;
}

return false;
}

SpirvConstant *ConstEvaluator::translateAPValue(const APValue &value,
const QualType targetType,
bool isSpecConstantMode) {
SpirvConstant *result = nullptr;

if (targetType->isBooleanType()) {
result = spvBuilder.getConstantBool(value.getInt().getBoolValue(),
isSpecConstantMode);
} else if (targetType->isIntegerType()) {
result = translateAPInt(value.getInt(), targetType, isSpecConstantMode);
} else if (targetType->isFloatingType()) {
result = translateAPFloat(value.getFloat(), targetType, isSpecConstantMode);
} else if (hlsl::IsHLSLVecType(targetType)) {
const QualType elemType = hlsl::GetHLSLVecElementType(targetType);
const auto numElements = value.getVectorLength();
// Special case for vectors of size 1. SPIR-V doesn't support this vector
// size so we need to translate it to scalar values.
if (numElements == 1) {
result =
translateAPValue(value.getVectorElt(0), elemType, isSpecConstantMode);
} else {
llvm::SmallVector<SpirvConstant *, 4> elements;
for (uint32_t i = 0; i < numElements; ++i) {
elements.push_back(translateAPValue(value.getVectorElt(i), elemType,
isSpecConstantMode));
}
result = spvBuilder.getConstantComposite(targetType, elements);
}
}

if (result)
return result;

emitError("APValue of type %0 unimplemented", {}) << value.getKind();
return 0;
}

SpirvConstant *ConstEvaluator::translateAPInt(const llvm::APInt &intValue,
QualType targetType,
bool isSpecConstantMode) {
return spvBuilder.getConstantInt(targetType, intValue, isSpecConstantMode);
}

SpirvConstant *ConstEvaluator::translateAPFloat(llvm::APFloat floatValue,
QualType targetType,
bool isSpecConstantMode) {
return spvBuilder.getConstantFloat(targetType, floatValue,
isSpecConstantMode);
}

SpirvConstant *ConstEvaluator::tryToEvaluateAsInt32(const llvm::APInt &intValue,
bool isSigned) {
if (isSigned && intValue.isSignedIntN(32)) {
return spvBuilder.getConstantInt(astContext.IntTy, intValue);
}
if (!isSigned && intValue.isIntN(32)) {
return spvBuilder.getConstantInt(astContext.UnsignedIntTy, intValue);
}

// Couldn't evaluate as a 32-bit int without losing information.
return nullptr;
}

SpirvConstant *
ConstEvaluator::tryToEvaluateAsFloat32(const llvm::APFloat &floatValue,
bool isSpecConstantMode) {
const auto &semantics = floatValue.getSemantics();
// If the given value is already a 32-bit float, there is no need to convert.
if (&semantics == &llvm::APFloat::IEEEsingle) {
return spvBuilder.getConstantFloat(astContext.FloatTy, floatValue,
isSpecConstantMode);
}

// Try to see if this literal float can be represented in 32-bit.
// Since the convert function below may modify the fp value, we call it on a
// temporary copy.
llvm::APFloat eval = floatValue;
bool losesInfo = false;
const auto convertStatus =
eval.convert(llvm::APFloat::IEEEsingle,
llvm::APFloat::rmNearestTiesToEven, &losesInfo);
if (convertStatus == llvm::APFloat::opOK && !losesInfo)
return spvBuilder.getConstantFloat(astContext.FloatTy,
llvm::APFloat(eval.convertToFloat()));

// Couldn't evaluate as a 32-bit float without losing information.
return nullptr;
}

SpirvConstant *ConstEvaluator::tryToEvaluateAsConst(const Expr *expr,
bool isSpecConstantMode) {
Expr::EvalResult evalResult;
if (expr->EvaluateAsRValue(evalResult, astContext) &&
!evalResult.HasSideEffects) {
return translateAPValue(evalResult.Val, expr->getType(),
isSpecConstantMode);
}

return nullptr;
}

} // namespace spirv
} // namespace clang
77 changes: 77 additions & 0 deletions tools/clang/lib/SPIRV/ConstEvaluator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//===-------- ConstEvaluator.h ----- Translate Constants --------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//===----------------------------------------------------------------------===//
//
// This file defines methods for translating AST expressions to SPIR-V
// constants.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_SPIRV_CONSTEVALUATOR_H
#define LLVM_CLANG_SPIRV_CONSTEVALUATOR_H

#include "clang/AST/ASTContext.h"
#include "clang/SPIRV/SpirvBuilder.h"
#include "llvm/ADT/APInt.h"

namespace clang {
namespace spirv {

class ConstEvaluator {
public:
ConstEvaluator(ASTContext &astContext, SpirvBuilder &spvBuilder)
: astContext(astContext), spvBuilder(spvBuilder) {}
/// Translates the given frontend APInt into its SPIR-V equivalent for the
/// given targetType.
SpirvConstant *translateAPInt(const llvm::APInt &intValue,
const QualType targetType,
bool isSpecConstantMode);

/// Translates the given frontend APFloat into its SPIR-V equivalent for the
/// given targetType.
SpirvConstant *translateAPFloat(llvm::APFloat floatValue, QualType targetType,
bool isSpecConstantMode);

/// Tries to evaluate the given APInt as a 32-bit integer. If the evaluation
/// can be performed without loss, it returns the <result-id> of the SPIR-V
/// constant for that value.
SpirvConstant *tryToEvaluateAsInt32(const llvm::APInt &intValue,
bool isSigned);

/// Tries to evaluate the given APFloat as a 32-bit float. If the evaluation
/// can be performed without loss, it returns the <result-id> of the SPIR-V
SpirvConstant *tryToEvaluateAsFloat32(const llvm::APFloat &floatValue,
bool isSpecConstantMode);

/// Tries to evaluate the given Expr as a constant and returns the <result-id>
/// if success. Otherwise, returns 0.
SpirvConstant *tryToEvaluateAsConst(const Expr *expr,
bool isSpecConstantMode);

private:
/// Translates the given frontend APValue into its SPIR-V equivalent for the
/// given targetType.
SpirvConstant *translateAPValue(const APValue &value,
const QualType targetType,
bool isSpecConstantMode);

/// Emits error to the diagnostic engine associated with the AST context.
template <unsigned N>
DiagnosticBuilder emitError(const char (&message)[N],
SourceLocation srcLoc = {}) {
const auto diagId = astContext.getDiagnostics().getCustomDiagID(
clang::DiagnosticsEngine::Error, message);
return astContext.getDiagnostics().Report(srcLoc, diagId);
}

ASTContext &astContext;
SpirvBuilder &spvBuilder;
};

} // namespace spirv
} // namespace clang

#endif // LLVM_CLANG_SPIRV_CONSTEVALUATOR_H
Loading
Loading