summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2020-02-06 14:31:09 -0500
committerGitHub <noreply@github.com>2020-02-06 14:31:09 -0500
commitd3331fba6eaab44646010b556106da38925d43e0 (patch)
treef54115540a457375a5d050bbfe1b04855b3f791b /source
parent9c84cceffba26817721a23a1a85a48644bf3a560 (diff)
Literal handling improvements (#1202)
* WIP: 64 literal diagnostic and truncation. * Improve how integer truncation is handled/supported. Added literal-int64.slang test. Set a suffix on all literals. Fixed problem on C++ based targets where l suffix was not the same as int() cast. So on C++ derived emitters, int() is used instead of l suffix to have same behavior across targets. * Add literal diagnostic testing. * Allow lexer to lex - in front of literals. * Fix lexing and converting int literal with -. * Too large small values of floats become inf. Handling writing inf types out on different targets. Add function to deterimine if a float literals kind. * Roll back the support of lexer lexing negative literals. * Fixed tests broken because of diagnostics numbers. Improved _isFinite * Fix compilation on linux. * Fix problem with abs on linux - use Math::Abs. * Fix typo. * * Improve warnings for float literals zeroed * Improved 64 bit type documentation * Handle half * Improved comments * Fixed tests broken * Use capital letters for suffixes. * Make default behavior on outputting a int literal that is an 'int32_t' is cast (not suffix) to avoid platform inconsistencies. Improve documentation for 64 bit types. Make tests cover material in docs. * Fixed tests. * Rename FloatKind::Normal -> Finite * Fix half zero check.
Diffstat (limited to 'source')
-rw-r--r--source/core/slang-math.h14
-rw-r--r--source/slang/slang-check-decl.cpp16
-rw-r--r--source/slang/slang-compiler.h30
-rw-r--r--source/slang/slang-diagnostic-defs.h7
-rw-r--r--source/slang/slang-diagnostics.cpp5
-rw-r--r--source/slang/slang-diagnostics.h2
-rw-r--r--source/slang/slang-emit-c-like.cpp36
-rw-r--r--source/slang/slang-emit-cpp.cpp48
-rw-r--r--source/slang/slang-emit-glsl.cpp51
-rw-r--r--source/slang/slang-emit-hlsl.cpp36
-rw-r--r--source/slang/slang-emit-hlsl.h1
-rw-r--r--source/slang/slang-ir.cpp32
-rw-r--r--source/slang/slang-ir.h15
-rw-r--r--source/slang/slang-parser.cpp172
-rw-r--r--source/slang/slang-stdlib.cpp8
-rw-r--r--source/slang/slang-syntax.cpp2
-rw-r--r--source/slang/slang.cpp62
17 files changed, 482 insertions, 55 deletions
diff --git a/source/core/slang-math.h b/source/core/slang-math.h
index 0daad0d5a..fa7eea6be 100644
--- a/source/core/slang-math.h
+++ b/source/core/slang-math.h
@@ -5,6 +5,13 @@
namespace Slang
{
+ // Some handy constants
+
+ // The largest positive (or negative) number
+# define SLANG_HALF_MAX 65504.0f
+ // Smallest (denormalized) value. 1 / 2^24
+# define SLANG_HALF_SUB_NORMAL_MIN (1.0f / 16777216.0f)
+
class Math
{
public:
@@ -20,6 +27,13 @@ namespace Slang
static const float Pi;
+
+ template <typename T>
+ static T Abs(T a)
+ {
+ return (a < 0) ? -a : a;
+ }
+
template<typename T>
static T Min(const T& v1, const T&v2)
{
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index dbc96f9b1..f8df46ff7 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -1697,21 +1697,7 @@ namespace Slang
bool SemanticsVisitor::isIntegerBaseType(BaseType baseType)
{
- switch(baseType)
- {
- default:
- return false;
-
- case BaseType::Int8:
- case BaseType::Int16:
- case BaseType::Int:
- case BaseType::Int64:
- case BaseType::UInt8:
- case BaseType::UInt16:
- case BaseType::UInt:
- case BaseType::UInt64:
- return true;
- }
+ return (BaseTypeInfo::getInfo(baseType).flags & BaseTypeInfo::Flag::Integer) != 0;
}
void SemanticsVisitor::validateEnumTagType(Type* type, SourceLoc const& loc)
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 2c58cb901..151e89b34 100644
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -1922,6 +1922,34 @@ namespace Slang
struct TypeCheckingCache;
//
+ // Information about BaseType that's useful for checking literals
+ struct BaseTypeInfo
+ {
+ typedef uint8_t Flags;
+ struct Flag
+ {
+ enum Enum : Flags
+ {
+ Signed = 0x1,
+ FloatingPoint = 0x2,
+ Integer = 0x4,
+ };
+ };
+
+ SLANG_FORCE_INLINE static const BaseTypeInfo& getInfo(BaseType baseType) { return s_info[Index(baseType)]; }
+
+ static UnownedStringSlice asText(BaseType baseType);
+
+ uint8_t sizeInBytes; ///< Size of type in bytes
+ Flags flags;
+ uint8_t baseType;
+
+ static bool check();
+
+ private:
+ static const BaseTypeInfo s_info[Index(BaseType::CountOf)];
+ };
+
class Session : public RefObject, public slang::IGlobalSession
{
public:
@@ -2014,7 +2042,7 @@ namespace Slang
RefPtr<Type> enumTypeType;
- Dictionary<int, RefPtr<Type>> builtinTypes;
+ RefPtr<Type> builtinTypes[Index(BaseType::CountOf)];
Dictionary<String, Decl*> magicDecls;
void initializeTypes();
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 430423464..b80650c05 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -317,6 +317,8 @@ DIAGNOSTIC(30600, Error, varWithoutTypeMustHaveInitializer, "a variable declarat
// 307xx: parameters
DIAGNOSTIC(30700, Error, outputParameterCannotHaveDefaultValue, "an 'out' or 'inout' parameter cannot have a default-value expression");
+// 39999 waiting to be placed in the right range
+
DIAGNOSTIC(39999, Error, expectedIntegerConstantWrongType, "expected integer constant (found: '$0')")
DIAGNOSTIC(39999, Error, expectedIntegerConstantNotConstant, "expression does not evaluate to a compile-time constant")
DIAGNOSTIC(39999, Error, expectedIntegerConstantNotLiteral, "could not extract value from integer constant")
@@ -345,7 +347,6 @@ DIAGNOSTIC(39999, Error, ambiguousExpression, "ambiguous reference");
DIAGNOSTIC(39999, Error, declarationDidntDeclareAnything, "declaration does not declare anything");
-
DIAGNOSTIC(39999, Error, expectedPrefixOperator, "function called as prefix operator was not declared `__prefix`")
DIAGNOSTIC(39999, Error, expectedPostfixOperator, "function called as postfix operator was not declared `__postfix`")
@@ -355,7 +356,11 @@ DIAGNOSTIC(39999, Error, tooManyArguments, "too many arguments to call (got $0,
DIAGNOSTIC(39999, Error, invalidIntegerLiteralSuffix, "invalid suffix '$0' on integer literal")
DIAGNOSTIC(39999, Error, invalidFloatingPointLiteralSuffix, "invalid suffix '$0' on floating-point literal")
+DIAGNOSTIC(39999, Warning, integerLiteralTruncated, "integer literal '$0' too large for type '$1' truncated to '$2'")
+DIAGNOSTIC(39999, Warning, floatLiteralUnrepresentable, "$0 literal '$1' unrepresentable, converted to '$2'")
+DIAGNOSTIC(39999, Warning, floatLiteralTooSmall, "'$1' is smaller than the smallest representable value for type $0, converted to '$2'")
+// 38xxx
DIAGNOSTIC(38000, Error, entryPointFunctionNotFound, "no function found matching entry point name '$0'")
DIAGNOSTIC(38001, Error, ambiguousEntryPoint, "more than one function matches entry point name '$0'")
diff --git a/source/slang/slang-diagnostics.cpp b/source/slang/slang-diagnostics.cpp
index 76b2fcffa..be799c3bd 100644
--- a/source/slang/slang-diagnostics.cpp
+++ b/source/slang/slang-diagnostics.cpp
@@ -43,6 +43,11 @@ void printDiagnosticArg(StringBuilder& sb, uint64_t val)
sb << val;
}
+void printDiagnosticArg(StringBuilder& sb, double val)
+{
+ sb << val;
+}
+
void printDiagnosticArg(StringBuilder& sb, Slang::String const& str)
{
sb << str;
diff --git a/source/slang/slang-diagnostics.h b/source/slang/slang-diagnostics.h
index 9b70ef0d9..385a40167 100644
--- a/source/slang/slang-diagnostics.h
+++ b/source/slang/slang-diagnostics.h
@@ -87,6 +87,8 @@ namespace Slang
void printDiagnosticArg(StringBuilder& sb, int64_t val);
void printDiagnosticArg(StringBuilder& sb, uint64_t val);
+ void printDiagnosticArg(StringBuilder& sb, double val);
+
void printDiagnosticArg(StringBuilder& sb, Slang::String const& str);
void printDiagnosticArg(StringBuilder& sb, Slang::UnownedStringSlice const& str);
void printDiagnosticArg(StringBuilder& sb, Name* name);
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index c2bfad0a7..a383caecf 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -756,27 +756,57 @@ void CLikeSourceEmitter::emitSimpleValueImpl(IRInst* inst)
case BaseType::Int16:
case BaseType::Int:
{
+ // NOTE! This hack is required, otherwise we get different results across targets.
+ // You'd hope that outputting L suffix would be enough to make this work, and not require a cast, but testing shows L suffix
+ // does not have the same meaning across targets.
+ //
+ // For example
+ //
+ // uint64_t v = 0x80000000;
+ //
+ // When output this becomes...
+ // v_0 = uint64_t(-2147483648L);
+ //
+ // On MSVC/Gcc/Clang this is equal to 0x80000000, elsewhere it's 0xffffffff80000000 elsewhere.
+ // Note that '-' isn't the issue because v0 = uint64_t(0x80000000L); produces the same issue
+ //
+ // If we use a cast, we get the same result across targets (which is why the hack is here).
+ //
+ // Why? It's not clear - it seems likely that it's related to the order of how the promotion takes place.
+ //
+ // If we convert from int32_t -> uint64_t, there are two possible scenarios
+ // 1) int32_t -> int64_t -> uint64_t (ie widen first then do sign type change)
+ // 2) int32_t -> uint32_t -> uint64_t (ie do sign type change then widen)
+ //
+ // 2 would produce what we see on C++, 1 what we see everywhere else.
+ //
+ // Why having a cast or having a suffix would make a difference though is not clear. It is also possible that the
+ // L suffix is just ignored, and the literal is really a 'non typed' int literal in C++.
+
+ m_writer->emit("int(");
m_writer->emit(litInst->value.intVal);
- break;
+ m_writer->emit(")");
+ return;
}
case BaseType::UInt8:
case BaseType::UInt16:
case BaseType::UInt:
{
m_writer->emit(UInt(litInst->value.intVal));
+ m_writer->emit("U");
break;
}
case BaseType::Int64:
{
m_writer->emitInt64(int64_t(litInst->value.intVal));
- m_writer->emit("ll");
+ m_writer->emit("LL");
break;
}
case BaseType::UInt64:
{
SLANG_COMPILE_TIME_ASSERT(sizeof(litInst->value.intVal) >= sizeof(uint64_t));
m_writer->emitUInt64(uint64_t(litInst->value.intVal));
- m_writer->emit("ull");
+ m_writer->emit("ULL");
break;
}
}
diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp
index 30388479e..ae1798382 100644
--- a/source/slang/slang-emit-cpp.cpp
+++ b/source/slang/slang-emit-cpp.cpp
@@ -1883,23 +1883,47 @@ void CPPSourceEmitter::emitSimpleFuncImpl(IRFunc* func)
void CPPSourceEmitter::emitSimpleValueImpl(IRInst* inst)
{
- switch (inst->op)
+ if (inst->op == kIROp_FloatLit)
{
- case kIROp_FloatLit:
+ IRConstant* constantInst = static_cast<IRConstant*>(inst);
+ switch (constantInst->getFloatKind())
{
- IRConstant* constantInst = static_cast<IRConstant*>(inst);
-
- m_writer->emit(constantInst->value.floatVal);
-
- // If the literal is a float, then we need to add 'f' at end
- IRType* type = constantInst->getDataType();
- if (type && type->op == kIROp_FloatType )
+ case IRConstant::FloatKind::Nan:
{
- m_writer->emitChar('f');
+ // TODO(JS):
+ // It's not clear this will work on all targets.
+ // In particular Visual Studio reports an error with this expression.
+ m_writer->emit("(0.0 / 0.0)");
+ break;
+ }
+ case IRConstant::FloatKind::PositiveInfinity:
+ {
+ m_writer->emit("SLANG_INFINITY");
+ break;
+ }
+ case IRConstant::FloatKind::NegativeInfinity:
+ {
+ m_writer->emit("(-SLANG_INFINITY)");
+ break;
+ }
+ default:
+ {
+ m_writer->emit(constantInst->value.floatVal);
+
+ // If the literal is a float, then we need to add 'f' at end, as
+ // without literal suffix the value defaults to double.
+ IRType* type = constantInst->getDataType();
+ if (type && type->op == kIROp_FloatType)
+ {
+ m_writer->emitChar('f');
+ }
+ break;
}
- break;
}
- default: Super::emitSimpleValueImpl(inst);
+ }
+ else
+ {
+ Super::emitSimpleValueImpl(inst);
}
}
diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp
index c319f44e9..fbae21f75 100644
--- a/source/slang/slang-emit-glsl.cpp
+++ b/source/slang/slang-emit-glsl.cpp
@@ -671,24 +671,69 @@ void GLSLSourceEmitter::emitSimpleValueImpl(IRInst* inst)
{
switch (type->getBaseType())
{
- default: break;
+ default:
+
+ case BaseType::Int8:
+ case BaseType::Int16:
+ case BaseType::Int:
+ {
+ m_writer->emit(litInst->value.intVal);
+ return;
+ }
+ case BaseType::UInt8:
+ case BaseType::UInt16:
+ case BaseType::UInt:
+ {
+ m_writer->emit(UInt(litInst->value.intVal));
+ m_writer->emit("U");
+ return;
+ }
case BaseType::Int64:
{
m_writer->emitInt64(int64_t(litInst->value.intVal));
- m_writer->emit("l");
+ m_writer->emit("L");
return;
}
case BaseType::UInt64:
{
SLANG_COMPILE_TIME_ASSERT(sizeof(litInst->value.intVal) >= sizeof(uint64_t));
m_writer->emitUInt64(uint64_t(litInst->value.intVal));
- m_writer->emit("ul");
+ m_writer->emit("UL");
return;
}
+
}
}
break;
}
+ case kIROp_FloatLit:
+ {
+ IRConstant* constantInst = static_cast<IRConstant*>(inst);
+
+ IRConstant::FloatKind kind = constantInst->getFloatKind();
+
+ switch (kind)
+ {
+ case IRConstant::FloatKind::Nan:
+ {
+ m_writer->emit("(0.0 / 0.0)");
+ return;
+ }
+ case IRConstant::FloatKind::PositiveInfinity:
+ {
+ m_writer->emit("(1.0 / 0.0)");
+ return;
+ }
+ case IRConstant::FloatKind::NegativeInfinity:
+ {
+ m_writer->emit("(-1.0 / 0.0)");
+ return;
+ }
+ default: break;
+ }
+ break;
+ }
+
default: break;
}
diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp
index 240ef6ad0..f615d41fb 100644
--- a/source/slang/slang-emit-hlsl.cpp
+++ b/source/slang/slang-emit-hlsl.cpp
@@ -488,6 +488,42 @@ void HLSLSourceEmitter::emitVectorTypeNameImpl(IRType* elementType, IRIntegerVal
m_writer->emit(">");
}
+void HLSLSourceEmitter::emitSimpleValueImpl(IRInst* inst)
+{
+ switch (inst->op)
+ {
+ case kIROp_FloatLit:
+ {
+ IRConstant* constantInst = static_cast<IRConstant*>(inst);
+ IRConstant::FloatKind kind = constantInst->getFloatKind();
+ switch (kind)
+ {
+ case IRConstant::FloatKind::Nan:
+ {
+ m_writer->emit("(0.0 / 0.0)");
+ return;
+ }
+ case IRConstant::FloatKind::PositiveInfinity:
+ {
+ m_writer->emit("(1.0 / 0.0)");
+ return;
+ }
+ case IRConstant::FloatKind::NegativeInfinity:
+ {
+ m_writer->emit("(-1.0 / 0.0)");
+ return;
+ }
+ default: break;
+ }
+ break;
+ }
+
+ default: break;
+ }
+
+ Super::emitSimpleValueImpl(inst);
+}
+
void HLSLSourceEmitter::emitSimpleTypeImpl(IRType* type)
{
switch (type->op)
diff --git a/source/slang/slang-emit-hlsl.h b/source/slang/slang-emit-hlsl.h
index 2120562e6..05fa3bb11 100644
--- a/source/slang/slang-emit-hlsl.h
+++ b/source/slang/slang-emit-hlsl.h
@@ -32,6 +32,7 @@ protected:
virtual void emitMatrixLayoutModifiersImpl(IRVarLayout* layout) SLANG_OVERRIDE;
virtual bool tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec) SLANG_OVERRIDE;
+ virtual void emitSimpleValueImpl(IRInst* inst) SLANG_OVERRIDE;
// Emit a single `register` semantic, as appropriate for a given resource-type-specific layout info
// Keyword to use in the uniform case (`register` for globals, `packoffset` inside a `cbuffer`)
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index 4f33e08ee..b121307ee 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -1674,6 +1674,38 @@ namespace Slang
}
}
+ bool IRConstant::isFinite() const
+ {
+ SLANG_ASSERT(op == kIROp_FloatLit);
+
+ // Lets check we can analyze as double, at least in principal
+ SLANG_COMPILE_TIME_ASSERT(sizeof(IRFloatingPointValue) == sizeof(double));
+ // We are in effect going to type pun (yay!), lets make sure they are the same size
+ SLANG_COMPILE_TIME_ASSERT(sizeof(IRIntegerValue) == sizeof(IRFloatingPointValue));
+
+ const uint64_t i = uint64_t(value.intVal);
+ int e = int(i >> 52) & 0x7ff;
+ return (e != 0x7ff);
+ }
+
+ IRConstant::FloatKind IRConstant::getFloatKind() const
+ {
+ SLANG_ASSERT(op == kIROp_FloatLit);
+
+ const uint64_t i = uint64_t(value.intVal);
+ int e = int(i >> 52) & 0x7ff;
+ if ( e == 0x7ff)
+ {
+ if (i << 12)
+ {
+ return FloatKind::Nan;
+ }
+ // Sign bit (top bit) will indicate positive or negative nan
+ return value.intVal < 0 ? FloatKind::NegativeInfinity : FloatKind::PositiveInfinity;
+ }
+ return FloatKind::Finite;
+ }
+
bool IRConstant::isValueEqual(IRConstant* rhs)
{
// If they are literally the same thing..
diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h
index ac7eab198..06dd0f94c 100644
--- a/source/slang/slang-ir.h
+++ b/source/slang/slang-ir.h
@@ -638,6 +638,14 @@ typedef double IRFloatingPointValue;
struct IRConstant : IRInst
{
+ enum class FloatKind
+ {
+ Finite,
+ PositiveInfinity,
+ NegativeInfinity,
+ Nan,
+ };
+
struct StringValue
{
uint32_t numChars; ///< The number of chars
@@ -665,6 +673,13 @@ struct IRConstant : IRInst
/// Returns a string slice (or empty string if not appropriate)
UnownedStringSlice getStringSlice();
+ /// Returns the kind of floating point value we have
+ FloatKind getFloatKind() const;
+
+ /// Returns true if the value is finite.
+ /// NOTE! Only works on floating point types
+ bool isFinite() const;
+
/// True if constants are equal
bool equal(IRConstant* rhs);
/// True if the value is equal.
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 9be2e49e5..17fe792ab 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -1,6 +1,7 @@
#include "slang-parser.h"
#include <assert.h>
+#include <float.h>
#include "slang-compiler.h"
#include "slang-lookup.h"
@@ -3871,6 +3872,20 @@ namespace Slang
return parseBoolLitExpr(parser, false);
}
+ static bool _isFinite(double value)
+ {
+ // Lets type pun double to uint64_t, so we can detect special double values
+ union
+ {
+ double d;
+ uint64_t i;
+ } u = { value };
+ // Detects nan and +-inf
+ const uint64_t i = u.i;
+ int e = int(i >> 52) & 0x7ff;
+ return (e != 0x7ff);
+ }
+
static RefPtr<Expr> parseAtomicExpr(Parser* parser)
{
switch( peekTokenType(parser) )
@@ -3961,9 +3976,14 @@ namespace Slang
char const* suffixCursor = suffix.begin();
const char*const suffixEnd = suffix.end();
- RefPtr<Type> suffixType = nullptr;
+ // If no suffix is defined go with the default
+ BaseType suffixBaseType = BaseType::Int;
+
if( suffixCursor < suffixEnd )
{
+ // Mark as void, taken as an error
+ suffixBaseType = BaseType::Void;
+
int lCount = 0;
int uCount = 0;
int unknownCount = 0;
@@ -3988,36 +4008,85 @@ namespace Slang
if(unknownCount)
{
parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix);
- suffixType = parser->getSession()->getErrorType();
+ suffixBaseType = BaseType::Void;
}
// `u` or `ul` suffix -> `uint`
else if(uCount == 1 && (lCount <= 1))
{
- suffixType = parser->getSession()->getUIntType();
+ suffixBaseType = BaseType::UInt;
}
// `l` suffix on integer -> `int` (== `long`)
else if(lCount == 1 && !uCount)
{
- suffixType = parser->getSession()->getIntType();
+ suffixBaseType = BaseType::Int;
}
// `ull` suffix -> `uint64_t`
else if(uCount == 1 && lCount == 2)
{
- suffixType = parser->getSession()->getUInt64Type();
+ suffixBaseType = BaseType::UInt64;
}
// `ll` suffix -> `int64_t`
else if(uCount == 0 && lCount == 2)
{
- suffixType = parser->getSession()->getInt64Type();
+ suffixBaseType = BaseType::Int64;
}
// TODO: do we need suffixes for smaller integer types?
else
{
parser->sink->diagnose(token, Diagnostics::invalidIntegerLiteralSuffix, suffix);
- suffixType = parser->getSession()->getErrorType();
+ suffixBaseType = BaseType::Void;
+ }
+ }
+
+ // TODO(JS):
+ // It is worth noting here that because of the way that the lexer works, that literals
+ // are always handled as if they are positive (a preceding - is taken as a negate on a
+ // positive value).
+ // The code here is designed to work with positive and negative values, as this behavior
+ // might change in the future, and is arguably more 'correct'.
+
+ const BaseTypeInfo& info = BaseTypeInfo::getInfo(suffixBaseType);
+ SLANG_ASSERT(info.flags & BaseTypeInfo::Flag::Integer);
+ SLANG_COMPILE_TIME_ASSERT(sizeof(value) == sizeof(uint64_t));
+
+ // If the type is 64 bits, do nothing, we'll assume all is good
+ if (suffixBaseType != BaseType::Void && info.sizeInBytes != sizeof(value))
+ {
+ const IntegerLiteralValue signBit = IntegerLiteralValue(1) << (8 * info.sizeInBytes - 1);
+ // Same as (~IntegerLiteralValue(0)) << (8 * info.sizeInBytes);, without the need for variable shift
+ const IntegerLiteralValue mask = -(signBit + signBit);
+
+ IntegerLiteralValue truncatedValue = value;
+ // If it's signed, and top bit is set, sign extend it negative
+ if (info.flags & BaseTypeInfo::Flag::Signed)
+ {
+ // Sign extend
+ truncatedValue = (value & signBit) ? (value | mask) : (value & ~mask);
+ }
+ else
+ {
+ // 0 top bits
+ truncatedValue = value & ~mask;
}
+
+ const IntegerLiteralValue maskedValue = value & mask;
+
+ // If the masked value is 0 or equal to the mask, we 'assume' no information is
+ // lost
+ // This allows for example -1u, to give 0xffffffff
+ // It also means 0xfffffffffffffffffu will give 0xffffffff, without a warning.
+ if (!(maskedValue == 0 || maskedValue == mask))
+ {
+ // Output a warning that number has been altered
+ parser->sink->diagnose(token, Diagnostics::integerLiteralTruncated, token.Content, BaseTypeInfo::asText(suffixBaseType), truncatedValue);
+ }
+
+ value = truncatedValue;
}
+ auto session = parser->getSession();
+ Type* suffixType = (suffixBaseType == BaseType::Void) ? session->getErrorType() : session->getBuiltinType(suffixBaseType);
+
constExpr->value = value;
constExpr->type = QualType(suffixType);
@@ -4040,7 +4109,9 @@ namespace Slang
char const* suffixCursor = suffix.begin();
const char*const suffixEnd = suffix.end();
- RefPtr<Type> suffixType = nullptr;
+
+ // Default is Float
+ BaseType suffixBaseType = BaseType::Float;
if( suffixCursor < suffixEnd )
{
int fCount = 0;
@@ -4072,32 +4143,105 @@ namespace Slang
if (unknownCount)
{
parser->sink->diagnose(token, Diagnostics::invalidFloatingPointLiteralSuffix, suffix);
- suffixType = parser->getSession()->getErrorType();
+ suffixBaseType = BaseType::Void;
}
// `f` suffix -> `float`
if(fCount == 1 && !lCount && !hCount)
{
- suffixType = parser->getSession()->getFloatType();
+ suffixBaseType = BaseType::Float;
}
// `l` or `lf` suffix on floating-point literal -> `double`
else if(lCount == 1 && (fCount <= 1))
{
- suffixType = parser->getSession()->getDoubleType();
+ suffixBaseType = BaseType::Double;
}
// `h` or `hf` suffix on floating-point literal -> `half`
else if(hCount == 1 && (fCount <= 1))
{
- suffixType = parser->getSession()->getHalfType();
+ suffixBaseType = BaseType::Half;
}
// TODO: are there other suffixes we need to handle?
else
{
parser->sink->diagnose(token, Diagnostics::invalidFloatingPointLiteralSuffix, suffix);
- suffixType = parser->getSession()->getErrorType();
+ suffixBaseType = BaseType::Void;
}
}
- constExpr->value = value;
+ // TODO(JS):
+ // It is worth noting here that because of the way that the lexer works, that literals
+ // are always handled as if they are positive (a preceding - is taken as a negate on a
+ // positive value).
+ // The code here is designed to work with positive and negative values, as this behavior
+ // might change in the future, and is arguably more 'correct'.
+
+ FloatingPointLiteralValue fixedValue = value;
+
+ // Check the value is finite for checking narrowing to literal type losing information
+ if (_isFinite(fixedValue))
+ {
+ switch (suffixBaseType)
+ {
+ case BaseType::Float:
+ {
+ // Fix out of range
+ if (fixedValue > FLT_MAX)
+ {
+ fixedValue = float(INFINITY);
+ }
+ else if (fixedValue < -FLT_MAX)
+ {
+ fixedValue = -float(INFINITY);
+ }
+ else if (fixedValue && float(fixedValue) == 0.0f)
+ {
+ fixedValue = 0.0f;
+ }
+ break;
+ }
+ case BaseType::Double:
+ {
+ break;
+ }
+ case BaseType::Half:
+ {
+ // Fix out of range
+ if (fixedValue > SLANG_HALF_MAX)
+ {
+ fixedValue = float(INFINITY);
+ }
+ else if (fixedValue < -SLANG_HALF_MAX)
+ {
+ fixedValue = -float(INFINITY);
+ }
+ else if (fixedValue && Math::Abs(fixedValue) < SLANG_HALF_SUB_NORMAL_MIN)
+ {
+ fixedValue = 0.0f;
+ }
+ break;
+ }
+ default: break;
+ }
+
+
+ if (fixedValue != value)
+ {
+ if (fixedValue == 0.0)
+ {
+ parser->sink->diagnose(token, Diagnostics::floatLiteralTooSmall, BaseTypeInfo::asText(suffixBaseType), token.Content, fixedValue);
+ }
+ else
+ {
+ parser->sink->diagnose(token, Diagnostics::floatLiteralUnrepresentable, BaseTypeInfo::asText(suffixBaseType), token.Content, fixedValue);
+ }
+ }
+ }
+
+ Session* session = parser->getSession();
+
+ Type* suffixType = (suffixBaseType == BaseType::Void) ? session->getErrorType() : session->getBuiltinType(suffixBaseType);
+
+ constExpr->value = fixedValue;
constExpr->type = QualType(suffixType);
return constExpr;
diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp
index 26a7dbb65..551a911c8 100644
--- a/source/slang/slang-stdlib.cpp
+++ b/source/slang/slang-stdlib.cpp
@@ -90,7 +90,7 @@ namespace Slang
// Here we declare the table of all our builtin types, so that we can generate all the relevant declarations.
//
- struct BaseTypeInfo
+ struct BaseTypeConversionInfo
{
char const* name;
BaseType tag;
@@ -98,7 +98,7 @@ namespace Slang
BaseTypeConversionKind conversionKind;
BaseTypeConversionRank conversionRank;
};
- static const BaseTypeInfo kBaseTypes[] = {
+ static const BaseTypeConversionInfo kBaseTypes[] = {
// TODO: `void` really shouldn't be in the `BaseType` enumeration, since it behaves so differently across the board
{ "void", BaseType::Void, 0, kBaseTypeConversionKind_Error, kBaseTypeConversionRank_Error},
@@ -121,8 +121,8 @@ namespace Slang
// Given two base types, we need to be able to compute the cost of converting between them.
ConversionCost getBaseTypeConversionCost(
- BaseTypeInfo const& toInfo,
- BaseTypeInfo const& fromInfo)
+ BaseTypeConversionInfo const& toInfo,
+ BaseTypeConversionInfo const& fromInfo)
{
if(toInfo.conversionKind == fromInfo.conversionKind
&& toInfo.conversionRank == fromInfo.conversionRank)
diff --git a/source/slang/slang-syntax.cpp b/source/slang/slang-syntax.cpp
index 19e4256fe..502425da8 100644
--- a/source/slang/slang-syntax.cpp
+++ b/source/slang/slang-syntax.cpp
@@ -236,7 +236,7 @@ void Type::accept(IValVisitor* visitor, void* extra)
Type* Session::getBuiltinType(BaseType flavor)
{
- return RefPtr<Type>(builtinTypes[(int)flavor]);
+ return builtinTypes[int(flavor)];
}
Type* Session::getInitializerListType()
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index bfc77b2e3..5fdec8259 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -37,6 +37,61 @@
namespace Slang {
+/* static */const BaseTypeInfo BaseTypeInfo::s_info[Index(BaseType::CountOf)] =
+{
+ { 0, 0, uint8_t(BaseType::Void) },
+ { uint8_t(sizeof(bool)), 0, uint8_t(BaseType::Bool) },
+ { uint8_t(sizeof(int8_t)), BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer , uint8_t(BaseType::Int8) },
+ { uint8_t(sizeof(int16_t)), BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer , uint8_t(BaseType::Int16) },
+ { uint8_t(sizeof(int32_t)), BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer , uint8_t(BaseType::Int) },
+ { uint8_t(sizeof(int64_t)), BaseTypeInfo::Flag::Signed | BaseTypeInfo::Flag::Integer , uint8_t(BaseType::Int64) },
+ { uint8_t(sizeof(uint8_t)), BaseTypeInfo::Flag::Integer , uint8_t(BaseType::UInt8) },
+ { uint8_t(sizeof(uint16_t)), BaseTypeInfo::Flag::Integer , uint8_t(BaseType::UInt16) },
+ { uint8_t(sizeof(uint32_t)), BaseTypeInfo::Flag::Integer , uint8_t(BaseType::UInt) },
+ { uint8_t(sizeof(uint64_t)), BaseTypeInfo::Flag::Integer, uint8_t(BaseType::UInt64) },
+ { uint8_t(sizeof(uint16_t)), BaseTypeInfo::Flag::FloatingPoint , uint8_t(BaseType::Half) },
+ { uint8_t(sizeof(float)), BaseTypeInfo::Flag::FloatingPoint , uint8_t(BaseType::Float) },
+ { uint8_t(sizeof(double)), BaseTypeInfo::Flag::FloatingPoint , uint8_t(BaseType::Double) },
+};
+
+/* static */bool BaseTypeInfo::check()
+{
+ for (Index i = 0; i < SLANG_COUNT_OF(s_info); ++i)
+ {
+ if (s_info[i].baseType != i)
+ {
+ SLANG_ASSERT(!"Inconsistency between the s_info table and BaseInfo");
+ return false;
+ }
+ }
+ return true;
+}
+
+/* static */UnownedStringSlice BaseTypeInfo::asText(BaseType baseType)
+{
+ switch (baseType)
+ {
+ case BaseType::Void: return UnownedStringSlice::fromLiteral("void");
+ case BaseType::Bool: return UnownedStringSlice::fromLiteral("bool");
+ case BaseType::Int8: return UnownedStringSlice::fromLiteral("int8_t");
+ case BaseType::Int16: return UnownedStringSlice::fromLiteral("int16_t");
+ case BaseType::Int: return UnownedStringSlice::fromLiteral("int");
+ case BaseType::Int64: return UnownedStringSlice::fromLiteral("int64_t");
+ case BaseType::UInt8: return UnownedStringSlice::fromLiteral("uint8_t");
+ case BaseType::UInt16: return UnownedStringSlice::fromLiteral("uint16_t");
+ case BaseType::UInt: return UnownedStringSlice::fromLiteral("uint");
+ case BaseType::UInt64: return UnownedStringSlice::fromLiteral("uint64_t");
+ case BaseType::Half: return UnownedStringSlice::fromLiteral("half");
+ case BaseType::Float: return UnownedStringSlice::fromLiteral("float");
+ case BaseType::Double: return UnownedStringSlice::fromLiteral("double");
+ default:
+ {
+ SLANG_ASSERT(!"Unknown basic type");
+ return UnownedStringSlice();
+ }
+ }
+}
+
// Allocate static const storage for the various interface IDs that the Slang API needs to expose
static const Guid IID_IComponentType = SLANG_UUID_IComponentType;
static const Guid IID_IEntryPoint = SLANG_UUID_IEntryPoint;
@@ -48,6 +103,8 @@ static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown;
void Session::init()
{
+ SLANG_ASSERT(BaseTypeInfo::check());
+
::memset(m_downstreamCompilerLocators, 0, sizeof(m_downstreamCompilerLocators));
DownstreamCompilerUtil::setDefaultLocators(m_downstreamCompilerLocators);
m_downstreamCompilerSet = new DownstreamCompilerSet;
@@ -2548,7 +2605,10 @@ Session::~Session()
destroyTypeCheckingCache();
- builtinTypes = decltype(builtinTypes)();
+ for (Index i = 0; i < SLANG_COUNT_OF(builtinTypes); ++i)
+ {
+ builtinTypes[i].setNull();
+ }
// destroy modules next
loadedModuleCode = decltype(loadedModuleCode)();
}