Skip to content

Commit

Permalink
[HLSL] Implement floating literal suffixes
Browse files Browse the repository at this point in the history
This change implements the HLSL floating literal suffixes for half and
double literals. The PR for the HLSL language specification for this
behavior is microsoft/hlsl-specs#175.

The TL;DR is that the `h` suffix on floating literals means `half`, and
the `l` suffix means `double`.

The expected behavior and diagnostics are different if native half is
supported. When native half is not enabled half is 32-bit so implicit
conversions to float do not lose precision and do not warn.

In all cases `half` and `float` are distinct types.

Resolves llvm#85712

../clang/test/SemaHLSL/literal_suffixes_no_16bit.hlsl
  • Loading branch information
llvm-beanz committed Apr 5, 2024
1 parent f905935 commit c4dd0ba
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 1 deletion.
5 changes: 4 additions & 1 deletion clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4117,14 +4117,17 @@ ExprResult Sema::ActOnNumericConstant(const Token &Tok, Scope *UDLScope) {
} else if (Literal.isFloatingLiteral()) {
QualType Ty;
if (Literal.isHalf){
if (getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()))
if (getLangOpts().HLSL ||
getOpenCLOptions().isAvailableOption("cl_khr_fp16", getLangOpts()))
Ty = Context.HalfTy;
else {
Diag(Tok.getLocation(), diag::err_half_const_requires_fp16);
return ExprError();
}
} else if (Literal.isFloat)
Ty = Context.FloatTy;
else if (getLangOpts().HLSL && Literal.isLong)
Ty = Context.DoubleTy;
else if (Literal.isLong)
Ty = Context.LongDoubleTy;
else if (Literal.isFloat16)
Expand Down
59 changes: 59 additions & 0 deletions clang/test/SemaHLSL/literal_suffixes.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -fnative-half-type -Wconversion -verify %s

void literal_assignments() {
half h;

h = 2.0h; // No conversion, no diagnostic expected.

// Literal conversions that don't lose precision also don't cause diagnostics.
// Conversion from double (no diagnostic expected)
h = 2.0l;
h = 2.0;
h = 2.0f;

// Literal assignments with conversions that lose precision produce
// diagnostics under `-Wconversion`.

// Lose precision on assignment.
h = 3.1415926535897932384626433h; // No diagnostic expected because this isn't a conversion.

// Lose precision on assignment converting float to half.
h = 3.1415926535897932384626433f; // expected-warning {{implicit conversion loses floating-point precision: 'float' to 'half'}}

// Lose precision on assignment converting float to half.
h = 3.1415926535897932384626433f * 2.0f; // expected-warning {{implicit conversion loses floating-point precision: 'float' to 'half'}}

// Lose precision on assignment converting double to half.
h = 3.1415926535897932384626433l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}

// Lose precision on assignment converting double to half.
h = 3.1415926535897932384626433l * 2.0l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}

// Literal assinments of values out of the representable range produce
// warnings.

h = 66000.h; // expected-warning {{magnitude of floating-point constant too large for type 'half'; maximum is 65504}}
h = -66000.h; // expected-warning {{magnitude of floating-point constant too large for type 'half'; maximum is 65504}}

// The `h` suffix is invalid on integer literals.
h = 66000h; // expected-error {{invalid suffix 'h' on integer constant}}
}

template <typename T, typename U>
struct is_same {
static const bool value = false;
};

template <typename T>
struct is_same<T, T> {
static const bool value = true;
};

// The no-suffix behavior is currently wrong. The behavior in DXC is complicated
// and undocumented. We have a language change planned to address this, and an
// issue tracking: https://github.com/llvm/llvm-project/issues/85714.
_Static_assert(is_same<double, __decltype(1.0)>::value, "1.0f literal is double (should be float)");

_Static_assert(is_same<half, __decltype(1.0h)>::value, "1.0h literal is half");
_Static_assert(is_same<float, __decltype(1.0f)>::value, "1.0f literal is float");
_Static_assert(is_same<double, __decltype(1.0l)>::value, "1.0l literal is double");
59 changes: 59 additions & 0 deletions clang/test/SemaHLSL/literal_suffixes_no_16bit.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.2-library -Wconversion -verify %s

void literal_assignments() {
half h;

h = 2.0h; // No conversion, no diagnostic expected.

// Literal conversions that don't lose precision also don't cause diagnostics.
// Conversion from double (no diagnostic expected)
h = 2.0l;
h = 2.0;
h = 2.0f;

// Literal assignments with conversions that lose precision produce
// diagnostics under `-Wconversion`.

// Lose precision on assignment.
h = 3.1415926535897932384626433h; // No diagnostic expected because this isn't a conversion.

// Lose precision on assignment converting float to half.
h = 3.1415926535897932384626433f; // No diagnostic expected because half and float are the same size.

// Lose precision on assignment converting float to half.
h = 3.1415926535897932384626433f * 2.0f; // No diagnostic expected because half and float are the same size.

// Lose precision on assignment converting double to half.
h = 3.1415926535897932384626433l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}

// Lose precision on assignment converting double to half.
h = 3.1415926535897932384626433l * 2.0l; // expected-warning {{implicit conversion loses floating-point precision: 'double' to 'half'}}

// Literal assinments of values out of the representable range produce
// warnings.

h = 66000.h; // No diagnostic expected because half is 32-bit.
h = -66000.h; // No diagnostic expected because half is 32-bit.

// The `h` suffix is invalid on integer literals.
h = 66000h; // expected-error {{invalid suffix 'h' on integer constant}}
}

template <typename T, typename U>
struct is_same {
static const bool value = false;
};

template <typename T>
struct is_same<T, T> {
static const bool value = true;
};

// The no-suffix behavior is currently wrong. The behavior in DXC is complicated
// and undocumented. We have a language change planned to address this, and an
// issue tracking: https://github.com/llvm/llvm-project/issues/85714.
_Static_assert(is_same<double, __decltype(1.0)>::value, "1.0f literal is double (should be float)");

_Static_assert(is_same<half, __decltype(1.0h)>::value, "1.0h literal is half");
_Static_assert(is_same<float, __decltype(1.0f)>::value, "1.0f literal is float");
_Static_assert(is_same<double, __decltype(1.0l)>::value, "1.0l literal is double");

0 comments on commit c4dd0ba

Please sign in to comment.