HLSL: add implicit promotions for assignments and function returns.
This commit is contained in:
parent
426542ba57
commit
c4a1307403
1688
Test/baseResults/hlsl.promotions.frag.out
Normal file
1688
Test/baseResults/hlsl.promotions.frag.out
Normal file
File diff suppressed because it is too large
Load Diff
201
Test/hlsl.promotions.frag
Normal file
201
Test/hlsl.promotions.frag
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
|
||||||
|
struct PS_OUTPUT
|
||||||
|
{
|
||||||
|
float4 Color : SV_Target0;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform int3 i3;
|
||||||
|
uniform bool3 b3;
|
||||||
|
uniform float3 f3;
|
||||||
|
uniform uint3 u3;
|
||||||
|
uniform double3 d3;
|
||||||
|
|
||||||
|
uniform int is;
|
||||||
|
uniform bool bs;
|
||||||
|
uniform float fs;
|
||||||
|
uniform uint us;
|
||||||
|
uniform double ds;
|
||||||
|
|
||||||
|
void Fn_F3(float3 x) { }
|
||||||
|
void Fn_I3(int3 x) { }
|
||||||
|
void Fn_U3(uint3 x) { }
|
||||||
|
void Fn_B3(bool3 x) { }
|
||||||
|
void Fn_D3(double3 x) { }
|
||||||
|
|
||||||
|
// ----------- Test implicit conversions on function returns -----------
|
||||||
|
float3 Fn_R_F3I(out float3 p) { p = i3; return i3; }
|
||||||
|
float3 Fn_R_F3U(out float3 p) { p = u3; return u3; }
|
||||||
|
float3 Fn_R_F3B(out float3 p) { p = b3; return b3; }
|
||||||
|
float3 Fn_R_F3D(out float3 p) { p = d3; return d3; } // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
int3 Fn_R_I3U(out int3 p) { p = u3; return u3; }
|
||||||
|
int3 Fn_R_I3B(out int3 p) { p = b3; return b3; }
|
||||||
|
int3 Fn_R_I3F(out int3 p) { p = f3; return f3; }
|
||||||
|
int3 Fn_R_I3D(out int3 p) { p = d3; return d3; } // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
uint3 Fn_R_U3I(out uint3 p) { p = i3; return i3; }
|
||||||
|
uint3 Fn_R_U3F(out uint3 p) { p = f3; return f3; }
|
||||||
|
uint3 Fn_R_U3B(out uint3 p) { p = b3; return b3; }
|
||||||
|
uint3 Fn_R_U3D(out uint3 p) { p = d3; return d3; } // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
bool3 Fn_R_B3I(out bool3 p) { p = i3; return i3; }
|
||||||
|
bool3 Fn_R_B3U(out bool3 p) { p = u3; return u3; }
|
||||||
|
bool3 Fn_R_B3F(out bool3 p) { p = f3; return f3; }
|
||||||
|
bool3 Fn_R_B3D(out bool3 p) { p = d3; return d3; }
|
||||||
|
|
||||||
|
double3 Fn_R_D3I(out double3 p) { p = i3; return i3; }
|
||||||
|
double3 Fn_R_D3U(out double3 p) { p = u3; return u3; }
|
||||||
|
double3 Fn_R_D3B(out double3 p) { p = b3; return b3; }
|
||||||
|
double3 Fn_R_D3F(out double3 p) { p = f3; return f3; }
|
||||||
|
|
||||||
|
PS_OUTPUT main()
|
||||||
|
{
|
||||||
|
// ----------- assignment conversions -----------
|
||||||
|
float3 r00 = i3;
|
||||||
|
float3 r01 = b3;
|
||||||
|
float3 r02 = u3;
|
||||||
|
float3 r03 = d3; // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
int3 r10 = b3;
|
||||||
|
int3 r11 = u3;
|
||||||
|
int3 r12 = f3;
|
||||||
|
int3 r13 = d3; // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
uint3 r20 = b3;
|
||||||
|
uint3 r21 = i3;
|
||||||
|
uint3 r22 = f3;
|
||||||
|
uint3 r23 = d3; // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
bool3 r30 = i3;
|
||||||
|
bool3 r31 = u3;
|
||||||
|
bool3 r32 = f3;
|
||||||
|
bool3 r33 = d3;
|
||||||
|
|
||||||
|
double3 r40 = i3;
|
||||||
|
double3 r41 = u3;
|
||||||
|
double3 r42 = f3;
|
||||||
|
double3 r43 = b3;
|
||||||
|
|
||||||
|
// ----------- assign ops: vector times vector -----------
|
||||||
|
r00 *= i3;
|
||||||
|
r01 *= b3;
|
||||||
|
r02 *= u3;
|
||||||
|
r03 *= d3; // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
r10 *= b3;
|
||||||
|
r11 *= u3;
|
||||||
|
r12 *= f3;
|
||||||
|
r13 *= d3; // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
r20 *= b3;
|
||||||
|
r21 *= i3;
|
||||||
|
r22 *= f3;
|
||||||
|
r23 *= d3; // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
// No mul operator for bools
|
||||||
|
|
||||||
|
r40 *= i3;
|
||||||
|
r41 *= u3;
|
||||||
|
r42 *= f3;
|
||||||
|
r43 *= b3;
|
||||||
|
|
||||||
|
// ----------- assign ops: vector times scalar -----------
|
||||||
|
r00 *= is;
|
||||||
|
r01 *= bs;
|
||||||
|
r02 *= us;
|
||||||
|
r03 *= ds; // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
r10 *= bs;
|
||||||
|
r11 *= us;
|
||||||
|
r12 *= fs;
|
||||||
|
r13 *= ds; // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
r20 *= bs;
|
||||||
|
r21 *= is;
|
||||||
|
r22 *= fs;
|
||||||
|
r23 *= ds; // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
// No mul operator for bools
|
||||||
|
|
||||||
|
r40 *= is;
|
||||||
|
r41 *= us;
|
||||||
|
r42 *= fs;
|
||||||
|
r43 *= bs;
|
||||||
|
|
||||||
|
|
||||||
|
#define FN_OVERLOADS 0 // change to 1 when overloads under promotions are in place
|
||||||
|
|
||||||
|
#if FN_OVERLOADS
|
||||||
|
Fn_F3(i3);
|
||||||
|
Fn_F3(u3);
|
||||||
|
Fn_F3(f3);
|
||||||
|
Fn_F3(b3);
|
||||||
|
Fn_F3(d3); // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
Fn_I3(i3);
|
||||||
|
Fn_I3(u3);
|
||||||
|
Fn_I3(f3);
|
||||||
|
Fn_I3(b3);
|
||||||
|
Fn_I3(d3); // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
Fn_U3(i3);
|
||||||
|
Fn_U3(u3);
|
||||||
|
Fn_U3(f3);
|
||||||
|
Fn_U3(b3);
|
||||||
|
Fn_U3(d3); // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
Fn_B3(i3);
|
||||||
|
Fn_B3(u3);
|
||||||
|
Fn_B3(f3);
|
||||||
|
Fn_B3(b3);
|
||||||
|
Fn_B3(d3);
|
||||||
|
|
||||||
|
Fn_D3(i3);
|
||||||
|
Fn_D3(u3);
|
||||||
|
Fn_D3(f3);
|
||||||
|
Fn_D3(b3);
|
||||||
|
Fn_D3(d3);
|
||||||
|
|
||||||
|
Fn_F3(i3.x);
|
||||||
|
Fn_F3(u3.x);
|
||||||
|
Fn_F3(f3.x);
|
||||||
|
Fn_F3(b3.x);
|
||||||
|
Fn_F3(d3.x); // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
Fn_I3(i3.x);
|
||||||
|
Fn_I3(u3.x);
|
||||||
|
Fn_I3(f3.x);
|
||||||
|
Fn_I3(b3.x);
|
||||||
|
Fn_I3(d3.x); // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
Fn_U3(i3.x);
|
||||||
|
Fn_U3(u3.x);
|
||||||
|
Fn_U3(f3.x);
|
||||||
|
Fn_U3(b3.x);
|
||||||
|
Fn_U3(d3.x); // valid, but loss of precision on downconversion.
|
||||||
|
|
||||||
|
Fn_B3(i3.x);
|
||||||
|
Fn_B3(u3.x);
|
||||||
|
Fn_B3(f3.x);
|
||||||
|
Fn_B3(b3.x);
|
||||||
|
Fn_B3(d3.x);
|
||||||
|
|
||||||
|
Fn_D3(i3.x);
|
||||||
|
Fn_D3(u3.x);
|
||||||
|
Fn_D3(f3.x);
|
||||||
|
Fn_D3(b3.x);
|
||||||
|
Fn_D3(d3.x);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const int si = 3;
|
||||||
|
const float sf = 1.2;
|
||||||
|
|
||||||
|
int c1 = si * sf; // 3.6 (not 3!)
|
||||||
|
int c2 = sf * si; // 3.6 (not 3!)
|
||||||
|
|
||||||
|
float4 outval = float4(si * sf, sf*si, c1, c2);
|
||||||
|
|
||||||
|
PS_OUTPUT psout;
|
||||||
|
psout.Color = outval;
|
||||||
|
return psout;
|
||||||
|
}
|
@ -524,7 +524,7 @@ TIntermTyped* TIntermediate::addConversion(TOperator op, const TType& type, TInt
|
|||||||
if (type.getBasicType() == node->getType().getBasicType())
|
if (type.getBasicType() == node->getType().getBasicType())
|
||||||
return node;
|
return node;
|
||||||
|
|
||||||
if (canImplicitlyPromote(node->getType().getBasicType(), type.getBasicType()))
|
if (canImplicitlyPromote(node->getType().getBasicType(), type.getBasicType(), op))
|
||||||
promoteTo = type.getBasicType();
|
promoteTo = type.getBasicType();
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
@ -726,11 +726,37 @@ TIntermTyped* TIntermediate::addShapeConversion(TOperator op, const TType& type,
|
|||||||
// See if the 'from' type is allowed to be implicitly converted to the
|
// See if the 'from' type is allowed to be implicitly converted to the
|
||||||
// 'to' type. This is not about vector/array/struct, only about basic type.
|
// 'to' type. This is not about vector/array/struct, only about basic type.
|
||||||
//
|
//
|
||||||
bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to) const
|
bool TIntermediate::canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op) const
|
||||||
{
|
{
|
||||||
if (profile == EEsProfile || version == 110)
|
if (profile == EEsProfile || version == 110)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Some languages allow more general (or potentially, more specific) conversions under some conditions.
|
||||||
|
if (source == EShSourceHlsl) {
|
||||||
|
const bool fromConvertable = (from == EbtFloat || from == EbtDouble || from == EbtInt || from == EbtUint || from == EbtBool);
|
||||||
|
const bool toConvertable = (to == EbtFloat || to == EbtDouble || to == EbtInt || to == EbtUint || to == EbtBool);
|
||||||
|
|
||||||
|
if (fromConvertable && toConvertable) {
|
||||||
|
switch (op) {
|
||||||
|
case EOpAndAssign: // assignments can perform arbitrary conversions
|
||||||
|
case EOpInclusiveOrAssign: // ...
|
||||||
|
case EOpExclusiveOrAssign: // ...
|
||||||
|
case EOpAssign: // ...
|
||||||
|
case EOpAddAssign: // ...
|
||||||
|
case EOpSubAssign: // ...
|
||||||
|
case EOpMulAssign: // ...
|
||||||
|
case EOpVectorTimesScalarAssign: // ...
|
||||||
|
case EOpMatrixTimesScalarAssign: // ...
|
||||||
|
case EOpDivAssign: // ...
|
||||||
|
case EOpModAssign: // ...
|
||||||
|
case EOpReturn: // function returns can also perform arbitrary conversions
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (to) {
|
switch (to) {
|
||||||
case EbtDouble:
|
case EbtDouble:
|
||||||
switch (from) {
|
switch (from) {
|
||||||
|
@ -189,7 +189,7 @@ public:
|
|||||||
TIntermTyped* addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, TSourceLoc);
|
TIntermTyped* addIndex(TOperator op, TIntermTyped* base, TIntermTyped* index, TSourceLoc);
|
||||||
TIntermTyped* addUnaryMath(TOperator, TIntermTyped* child, TSourceLoc);
|
TIntermTyped* addUnaryMath(TOperator, TIntermTyped* child, TSourceLoc);
|
||||||
TIntermTyped* addBuiltInFunctionCall(const TSourceLoc& line, TOperator, bool unary, TIntermNode*, const TType& returnType);
|
TIntermTyped* addBuiltInFunctionCall(const TSourceLoc& line, TOperator, bool unary, TIntermNode*, const TType& returnType);
|
||||||
bool canImplicitlyPromote(TBasicType from, TBasicType to) const;
|
bool canImplicitlyPromote(TBasicType from, TBasicType to, TOperator op = EOpNull) const;
|
||||||
TOperator mapTypeToConstructorOp(const TType&) const;
|
TOperator mapTypeToConstructorOp(const TType&) const;
|
||||||
TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right);
|
TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right);
|
||||||
TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc&);
|
TIntermAggregate* growAggregate(TIntermNode* left, TIntermNode* right, const TSourceLoc&);
|
||||||
|
@ -121,6 +121,7 @@ INSTANTIATE_TEST_CASE_P(
|
|||||||
{"hlsl.load.offsetarray.dx10.frag", "main"},
|
{"hlsl.load.offsetarray.dx10.frag", "main"},
|
||||||
{"hlsl.numericsuffixes.frag", "main"},
|
{"hlsl.numericsuffixes.frag", "main"},
|
||||||
{"hlsl.pp.line.frag", "main"},
|
{"hlsl.pp.line.frag", "main"},
|
||||||
|
{"hlsl.promotions.frag", "main"},
|
||||||
{"hlsl.sample.array.dx10.frag", "main"},
|
{"hlsl.sample.array.dx10.frag", "main"},
|
||||||
{"hlsl.sample.basic.dx10.frag", "main"},
|
{"hlsl.sample.basic.dx10.frag", "main"},
|
||||||
{"hlsl.sample.offset.dx10.frag", "main"},
|
{"hlsl.sample.offset.dx10.frag", "main"},
|
||||||
|
@ -2446,7 +2446,7 @@ bool HlslGrammar::acceptJumpStatement(TIntermNode*& statement)
|
|||||||
TIntermTyped* node;
|
TIntermTyped* node;
|
||||||
if (acceptExpression(node)) {
|
if (acceptExpression(node)) {
|
||||||
// hook it up
|
// hook it up
|
||||||
statement = intermediate.addBranch(EOpReturn, node, token.loc);
|
statement = parseContext.handleReturnValue(token.loc, node);
|
||||||
} else
|
} else
|
||||||
statement = intermediate.addBranch(EOpReturn, token.loc);
|
statement = intermediate.addBranch(EOpReturn, token.loc);
|
||||||
break;
|
break;
|
||||||
|
@ -796,6 +796,25 @@ TIntermAggregate* HlslParseContext::handleFunctionDefinition(const TSourceLoc& l
|
|||||||
return paramNodes;
|
return paramNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle function returns, including type conversions to the function return type
|
||||||
|
// if necessary.
|
||||||
|
TIntermNode* HlslParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value)
|
||||||
|
{
|
||||||
|
if (currentFunctionType->getBasicType() == EbtVoid) {
|
||||||
|
error(loc, "void function cannot return a value", "return", "");
|
||||||
|
return intermediate.addBranch(EOpReturn, loc);
|
||||||
|
} else if (*currentFunctionType != value->getType()) {
|
||||||
|
TIntermTyped* converted = intermediate.addConversion(EOpReturn, *currentFunctionType, value);
|
||||||
|
if (converted) {
|
||||||
|
return intermediate.addBranch(EOpReturn, converted, loc);
|
||||||
|
} else {
|
||||||
|
error(loc, "type does not match, or is not convertible to, the function's return type", "return", "");
|
||||||
|
return intermediate.addBranch(EOpReturn, value, loc);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return intermediate.addBranch(EOpReturn, value, loc);
|
||||||
|
}
|
||||||
|
|
||||||
void HlslParseContext::handleFunctionArgument(TFunction* function, TIntermTyped*& arguments, TIntermTyped* newArg)
|
void HlslParseContext::handleFunctionArgument(TFunction* function, TIntermTyped*& arguments, TIntermTyped* newArg)
|
||||||
{
|
{
|
||||||
TParameter param = { 0, new TType };
|
TParameter param = { 0, new TType };
|
||||||
|
@ -85,6 +85,7 @@ public:
|
|||||||
TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
|
TIntermTyped* handleDotDereference(const TSourceLoc&, TIntermTyped* base, const TString& field);
|
||||||
TFunction* handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
|
TFunction* handleFunctionDeclarator(const TSourceLoc&, TFunction& function, bool prototype);
|
||||||
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&);
|
TIntermAggregate* handleFunctionDefinition(const TSourceLoc&, TFunction&);
|
||||||
|
TIntermNode* handleReturnValue(const TSourceLoc&, TIntermTyped*);
|
||||||
void handleFunctionArgument(TFunction*, TIntermTyped*& arguments, TIntermTyped* newArg);
|
void handleFunctionArgument(TFunction*, TIntermTyped*& arguments, TIntermTyped* newArg);
|
||||||
TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*);
|
TIntermTyped* handleFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*);
|
||||||
void decomposeIntrinsic(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
|
void decomposeIntrinsic(const TSourceLoc&, TIntermTyped*& node, TIntermNode* arguments);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user