Skip to content

Commit 16cd361

Browse files
authored
Be lenient on same-size unsigend->signed conversion. (shader-slang#2913)
* Be lenient on same-size unsigend->signed conversion. * Fix tests. * Use 250. * wip * Fix. * Fix tests. * Fix. --------- Co-authored-by: Yong He <yhe@nvidia.com>
1 parent a7ed48b commit 16cd361

10 files changed

+151
-38
lines changed

source/slang/slang-ast-support-types.h

+7
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ namespace Slang
8080
// from one on `Foo`
8181
kConversionCost_GetRef = 5,
8282
kConversionCost_ImplicitDereference = 10,
83+
kConversionCost_InRangeIntLitConversion = 23,
84+
kConversionCost_InRangeIntLitSignedToUnsignedConversion = 32,
85+
kConversionCost_InRangeIntLitUnsignedToSignedConversion = 81,
86+
8387

8488
// Conversions based on explicit sub-typing relationships are the cheapest
8589
//
@@ -97,6 +101,9 @@ namespace Slang
97101
// Conversions that are lossless, but change "kind"
98102
kConversionCost_UnsignedToSignedPromotion = 200,
99103

104+
// Same-size size unsigned->signed conversions are potentially lossy, but they are commonly allowed silently.
105+
kConversionCost_SameSizeUnsignedToSignedConversion = 250,
106+
100107
// Conversion from signed->unsigned integer of same or greater size
101108
kConversionCost_SignedToUnsignedConversion = 300,
102109

source/slang/slang-check-conversion.cpp

+78-5
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,79 @@ namespace Slang
628628
}
629629
}
630630

631+
static bool isSigned(Type* t)
632+
{
633+
auto basicType = as<BasicExpressionType>(t);
634+
if (!basicType) return false;
635+
switch (basicType->baseType)
636+
{
637+
case BaseType::Int8:
638+
case BaseType::Int16:
639+
case BaseType::Int:
640+
case BaseType::Int64:
641+
case BaseType::IntPtr:
642+
return true;
643+
default:
644+
return false;
645+
}
646+
}
647+
648+
static int getTypeBitSize(Type* t)
649+
{
650+
auto basicType = as<BasicExpressionType>(t);
651+
if (!basicType) return 0;
652+
653+
switch (basicType->baseType)
654+
{
655+
case BaseType::Int8:
656+
case BaseType::UInt8:
657+
return 8;
658+
case BaseType::Int16:
659+
case BaseType::UInt16:
660+
return 16;
661+
case BaseType::Int:
662+
case BaseType::UInt:
663+
return 32;
664+
case BaseType::Int64:
665+
case BaseType::UInt64:
666+
return 64;
667+
case BaseType::IntPtr:
668+
case BaseType::UIntPtr:
669+
#if SLANG_PTR_IS_32
670+
return 32;
671+
#else
672+
return 64;
673+
#endif
674+
default:
675+
return 0;
676+
}
677+
}
678+
679+
ConversionCost SemanticsVisitor::getImplicitConversionCostWithKnownArg(Decl* decl, Type* toType, Expr* arg)
680+
{
681+
ConversionCost candidateCost = getImplicitConversionCost(decl);
682+
683+
// Fix up the cost if the operand is a const lit.
684+
if (isScalarIntegerType(toType))
685+
{
686+
auto knownVal = as<IntegerLiteralExpr>(arg);
687+
if (!knownVal)
688+
return candidateCost;
689+
if (getIntValueBitSize(knownVal->value) <= getTypeBitSize(toType))
690+
{
691+
bool toTypeIsSigned = isSigned(toType);
692+
bool fromTypeIsSigned = isSigned(knownVal->type);
693+
if (toTypeIsSigned == fromTypeIsSigned)
694+
candidateCost = kConversionCost_InRangeIntLitConversion;
695+
else if (toTypeIsSigned)
696+
candidateCost = kConversionCost_InRangeIntLitUnsignedToSignedConversion;
697+
else
698+
candidateCost = kConversionCost_InRangeIntLitSignedToUnsignedConversion;
699+
}
700+
}
701+
return candidateCost;
702+
}
703+
631704
bool SemanticsVisitor::_coerce(
632705
CoercionSite site,
633706
Type* toType,
@@ -989,8 +1062,8 @@ namespace Slang
9891062
ConversionCost bestCost = kConversionCost_Explicit;
9901063
for(auto candidate : overloadContext.bestCandidates)
9911064
{
992-
ConversionCost candidateCost = getImplicitConversionCost(
993-
candidate.item.declRef.getDecl());
1065+
ConversionCost candidateCost = getImplicitConversionCostWithKnownArg(
1066+
candidate.item.declRef.getDecl(), toType, fromExpr);
9941067

9951068
if(candidateCost < bestCost)
9961069
bestCost = candidateCost;
@@ -1027,8 +1100,8 @@ namespace Slang
10271100
// Next, we need to look at the implicit conversion
10281101
// cost associated with the initializer we are invoking.
10291102
//
1030-
ConversionCost cost = getImplicitConversionCost(
1031-
overloadContext.bestCandidate->item.declRef.getDecl());
1103+
ConversionCost cost = getImplicitConversionCostWithKnownArg(
1104+
overloadContext.bestCandidate->item.declRef.getDecl(), toType, fromExpr);
10321105

10331106
// If the cost is too high to be usable as an
10341107
// implicit conversion, then we will report the
@@ -1149,7 +1222,7 @@ namespace Slang
11491222

11501223
BasicTypeKeyPair cacheKey;
11511224
cacheKey.type1 = makeBasicTypeKey(toType);
1152-
cacheKey.type2 = makeBasicTypeKey(fromType);
1225+
cacheKey.type2 = makeBasicTypeKey(fromType, fromExpr);
11531226

11541227
if( cacheKey.isValid())
11551228
{

source/slang/slang-check-impl.h

+54-15
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,60 @@ namespace Slang
2626
/// Note: this currently does not include PtrTypeBase.
2727
Type* getPointedToTypeIfCanImplicitDeref(Type* type);
2828

29+
inline int getIntValueBitSize(IntegerLiteralValue val)
30+
{
31+
uint64_t v = val > 0 ? (uint64_t)val : (uint64_t)-val;
32+
int result = 1;
33+
while (v >>= 1)
34+
{
35+
result++;
36+
}
37+
return result;
38+
}
39+
2940
// A flat representation of basic types (scalars, vectors and matrices)
3041
// that can be used as lookup key in caches
31-
enum class BasicTypeKey : uint16_t
42+
struct BasicTypeKey
3243
{
33-
Invalid = 0xffff, ///< Value that can never be a valid type
44+
uint32_t baseType : 8;
45+
uint32_t dim1 : 4;
46+
uint32_t dim2 : 4;
47+
uint32_t knownConstantBitCount : 8;
48+
uint32_t knownNegative : 1;
49+
uint32_t reserved : 7;
50+
uint32_t getRaw() const
51+
{
52+
uint32_t val;
53+
memcpy(&val, this, sizeof(uint32_t));
54+
return val;
55+
}
56+
bool operator==(BasicTypeKey other) const
57+
{
58+
return getRaw() == other.getRaw();
59+
}
60+
static BasicTypeKey invalid() { return BasicTypeKey{ 0xff, 0, 0 }; }
3461
};
3562

3663
SLANG_FORCE_INLINE BasicTypeKey makeBasicTypeKey(BaseType baseType, IntegerLiteralValue dim1 = 0, IntegerLiteralValue dim2 = 0)
3764
{
3865
SLANG_ASSERT(dim1 >= 0 && dim2 >= 0);
39-
return BasicTypeKey((uint8_t(baseType) << 8) | (uint8_t(dim1) << 4) | uint8_t(dim2));
66+
return BasicTypeKey{ uint8_t(baseType), uint8_t(dim1), uint8_t(dim2) };
4067
}
4168

42-
inline BasicTypeKey makeBasicTypeKey(Type* typeIn)
69+
inline BasicTypeKey makeBasicTypeKey(Type* typeIn, Expr* exprIn = nullptr)
4370
{
4471
if (auto basicType = as<BasicExpressionType>(typeIn))
4572
{
46-
return makeBasicTypeKey(basicType->baseType);
73+
auto rs = makeBasicTypeKey(basicType->baseType);
74+
if (auto constInt = as<IntegerLiteralExpr>(exprIn))
75+
{
76+
if (constInt->value < 0)
77+
{
78+
rs.knownNegative = 1;
79+
}
80+
rs.knownConstantBitCount = getIntValueBitSize(constInt->value);
81+
}
82+
return rs;
4783
}
4884
else if (auto vectorType = as<VectorExpressionType>(typeIn))
4985
{
@@ -68,7 +104,7 @@ namespace Slang
68104
}
69105
}
70106
}
71-
return BasicTypeKey::Invalid;
107+
return BasicTypeKey::invalid();
72108
}
73109

74110
struct BasicTypeKeyPair
@@ -77,11 +113,11 @@ namespace Slang
77113
bool operator==(const BasicTypeKeyPair& rhs) const { return type1 == rhs.type1 && type2 == rhs.type2; }
78114
bool operator!=(const BasicTypeKeyPair& rhs) const { return !(*this == rhs); }
79115

80-
bool isValid() const { return type1 != BasicTypeKey::Invalid && type2 != BasicTypeKey::Invalid; }
116+
bool isValid() const { return type1.getRaw() != BasicTypeKey::invalid().getRaw() && type2.getRaw() != BasicTypeKey::invalid().getRaw(); }
81117

82118
HashCode getHashCode()
83119
{
84-
return HashCode(int(type1) << 16 | int(type2));
120+
return combineHash(type1.getRaw(), type2.getRaw());
85121
}
86122
};
87123

@@ -95,25 +131,25 @@ namespace Slang
95131
}
96132
HashCode getHashCode()
97133
{
98-
return HashCode(((int)(UInt64)(void*)(operatorName) << 16) ^ (int(args[0]) << 8) ^ (int(args[1])));
134+
return combineHash((int)(UInt64)(void*)(operatorName), args[0].getRaw(), args[1].getRaw());
99135
}
100136
bool fromOperatorExpr(OperatorExpr* opExpr)
101137
{
102138
// First, lets see if the argument types are ones
103139
// that we can encode in our space of keys.
104-
args[0] = BasicTypeKey::Invalid;
105-
args[1] = BasicTypeKey::Invalid;
140+
args[0] = BasicTypeKey::invalid();
141+
args[1] = BasicTypeKey::invalid();
106142
if (opExpr->arguments.getCount() > 2)
107143
return false;
108144

109145
for (Index i = 0; i < opExpr->arguments.getCount(); i++)
110146
{
111-
auto key = makeBasicTypeKey(opExpr->arguments[i]->type.Ptr());
112-
if (key == BasicTypeKey::Invalid)
147+
auto key = makeBasicTypeKey(opExpr->arguments[i]->type.Ptr(), opExpr->arguments[i]);
148+
if (key.getRaw() == BasicTypeKey::invalid().getRaw())
113149
{
114150
return false;
115151
}
116-
args[i]= key;
152+
args[i] = key;
117153
}
118154

119155
// Next, lets see if we can find an intrinsic opcode
@@ -136,7 +172,7 @@ namespace Slang
136172
// Look at a candidate definition to be called and
137173
// see if it gives us a key to work with.
138174
//
139-
Decl* funcDecl = overloadedBase->lookupResult2.item.declRef.decl;
175+
Decl* funcDecl = item.declRef.decl;
140176
if (auto genDecl = as<GenericDecl>(funcDecl))
141177
funcDecl = genDecl->inner;
142178

@@ -783,6 +819,9 @@ namespace Slang
783819
ConversionCost getImplicitConversionCost(
784820
Decl* decl);
785821

822+
ConversionCost getImplicitConversionCostWithKnownArg(Decl* decl, Type* toType, Expr* arg);
823+
824+
786825
BuiltinConversionKind getImplicitConversionBuiltinKind(
787826
Decl* decl);
788827

source/slang/slang-stdlib.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,15 @@ namespace Slang
156156
{
157157
return kConversionCost_UnsignedToSignedPromotion;
158158
}
159+
// Same-size unsigned to signed integer conversion.
160+
else if (toInfo.conversionKind == kBaseTypeConversionKind_Signed
161+
&& fromInfo.conversionKind == kBaseTypeConversionKind_Unsigned
162+
&& toInfo.conversionRank == fromInfo.conversionRank
163+
&& toInfo.conversionRank != kBaseTypeConversionRank_IntPtr
164+
&& fromInfo.conversionRank != kBaseTypeConversionRank_IntPtr)
165+
{
166+
return kConversionCost_SameSizeUnsignedToSignedConversion;
167+
}
159168

160169
// Conversion from signed to unsigned is always lossy,
161170
// but it is preferred over conversions from unsigned

tests/compute/unbounded-array-of-array-syntax.slang.glsl

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void main()
2424
(bufferCount_0) = (g_aoa_0[_S3])._data.length(); (bufferStride_0) = 0;
2525

2626
int innerIndex_1;
27-
if(uint(innerIndex_0) >= bufferCount_0)
27+
if(innerIndex_0 >= int(bufferCount_0))
2828
{
2929
innerIndex_1 = int(bufferCount_0 - 1U);
3030
}

tests/compute/unbounded-array-of-array-syntax.slang.hlsl

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void computeMain(vector<uint,3> dispatchThreadID_0 : SV_DISPATCHTHREADID)
2424

2525
uint bufferCount_0 = _S1;
2626

27-
if((uint) innerIndex_1 >= bufferCount_0)
27+
if(innerIndex_1 >= (int)bufferCount_0)
2828
{
2929
innerIndex_0 = (int) (bufferCount_0 - (uint) 1);
3030
}

tests/diagnostics/constexpr-error.slang.expected

-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
result code = -1
22
standard error = {
3-
tests/diagnostics/constexpr-error.slang(27): warning 30081: implicit conversion from 'vector<uint,2>' to 'vector<int,2>' is not recommended
4-
result += t.Sample(s, uv, offset);
5-
^~~~~~
63
tests/diagnostics/constexpr-error.slang(27): error 40006: expected a compile-time constant
74
result += t.Sample(s, uv, offset);
85
^~~~~~

tests/diagnostics/enum-implicit-conversion.slang.expected

-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
result code = -1
22
standard error = {
3-
tests/diagnostics/enum-implicit-conversion.slang(18): warning 30081: implicit conversion from 'uint' to 'int' is not recommended
4-
int foo(uint x) { return x * 256 * 16; }
5-
^
6-
tests/diagnostics/enum-implicit-conversion.slang(22): warning 30081: implicit conversion from 'uint' to 'int' is not recommended
7-
int bar(uint x) { return x * 256 * 256 * 16; }
8-
^
93
tests/diagnostics/enum-implicit-conversion.slang(27): error 30019: expected an expression of type 'Color', got 'int'
104
Color c = val;
115
^~~
@@ -23,9 +17,6 @@ tests/diagnostics/enum-implicit-conversion.slang(42): error 39999: ambiguous cal
2317
^
2418
tests/diagnostics/enum-implicit-conversion.slang(18): note 39999: candidate: func foo(uint) -> int
2519
tests/diagnostics/enum-implicit-conversion.slang(17): note 39999: candidate: func foo(int) -> int
26-
tests/diagnostics/enum-implicit-conversion.slang(47): warning 30081: implicit conversion from 'uint' to 'int' is not recommended
27-
return x + y + z;
28-
^
2920
}
3021
standard output = {
3122
}

tests/diagnostics/generic-invalid-type-specialization.slang.expected

-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
result code = -1
22
standard error = {
3-
tests/diagnostics/generic-invalid-type-specialization.slang(14): warning 30081: implicit conversion from 'uint' to 'int' is not recommended
4-
int index = dispatchThreadID.x;
5-
^
63
tests/diagnostics/generic-invalid-type-specialization.slang(17): error 30060: expected a type, got a 'int'
74
Check<2 + 2> v;
85
^

tests/experimental/liveness/liveness.slang.expected

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ int someSlowFunc_0(int a_0)
4747
uint _S5 = v_0 >> 1;
4848
uint _S6 = v_0;
4949
livenessEnd_1(v_0, 0);
50-
uint _S7 = (_S5 | _S6 << 31) * uint(i_0);
50+
uint _S7 = uint(int(_S5 | _S6 << 31) * i_0);
5151
int i_1 = i_0 + 1;
5252
livenessStart_0(v_0, 0);
5353
v_0 = _S7;

0 commit comments

Comments
 (0)