Skip to content

[HLSL] Add "or" intrinsic #128979

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

Merged
merged 13 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from 7 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
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4783,6 +4783,12 @@ def HLSLAnd : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}

def HLSLOr : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_or"];
let Attributes = [NoThrow, Const];
let Prototype = "void(...)";
}

def HLSLAny : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_any"];
let Attributes = [NoThrow, Const];
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19492,6 +19492,11 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
Value *Op1 = EmitScalarExpr(E->getArg(1));
return Builder.CreateAnd(Op0, Op1, "hlsl.and");
}
case Builtin::BI__builtin_hlsl_or: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
Value *Op1 = EmitScalarExpr(E->getArg(1));
return Builder.CreateOr(Op0, Op1, "hlsl.or");
}
case Builtin::BI__builtin_hlsl_any: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
return Builder.CreateIntrinsic(
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,24 @@ _HLSL_BUILTIN_ALIAS(__builtin_hlsl_and)
bool4 and(bool4 x, bool4 y);
// clang-format on

//===----------------------------------------------------------------------===//
// or builtins
//===----------------------------------------------------------------------===//

/// \fn T or(T x, T y)
/// \brief Returns the bitwise OR of the two input values, \a x and \a y.
/// \param x The first input value and y The second input value.
///
/// \returns The logically OR a vector and retuens bool vector.

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_or)
bool or(bool, bool);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_or)
bool2 or(bool2, bool2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_or)
bool3 or(bool3, bool3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_or)
bool4 or(bool4, bool4);
//===----------------------------------------------------------------------===//
// any builtins
//===----------------------------------------------------------------------===//
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2305,6 +2305,25 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
TheCall->setType(ArgTyA);
break;
}
case Builtin::BI__builtin_hlsl_or: {
if (SemaRef.checkArgCount(TheCall, 2))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are doing the same sema checks for the and intrinsic. Delete this whole block and merge the and an or builtins as fall through cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code updated with suggested changes.

return true;
if (CheckVectorElementCallArgs(&SemaRef, TheCall))
return true;

// Ensure input expr type is a scalar/vector and the same as the return type
if (CheckScalarOrVector(&SemaRef, TheCall, getASTContext().BoolTy, 0))
return true;

// Ensure input parameter type is bool
ExprResult A = TheCall->getArg(0);
QualType ArgTyA = A.get()->getType();

// return type is the same as the input type
TheCall->setType(ArgTyA);

break;
}
case Builtin::BI__builtin_hlsl_all:
case Builtin::BI__builtin_hlsl_any: {
if (SemaRef.checkArgCount(TheCall, 1))
Expand Down
85 changes: 85 additions & 0 deletions clang/test/CodeGenHLSL/builtins/or.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// RUN: %clang_cc1 -finclude-default-header -triple \
// RUN: dxil-pc-shadermodel6.3-library %s \
// RUN: -emit-llvm -O1 -o - | FileCheck %s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose you are doing O1 because you want to see short circuiting optimizations? I think this would better to check we are doing thing correctly without any optimizations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test cases updated without any optimization


//CHECK-LABEL: define noundef i1 @_Z12test_or_boolbb(
//CHECK-SAME: i1 noundef [[X:%.*]], i1 noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
//CHECK-NEXT: [[ENTRY:.*:]]
//CHECK-NEXT: [[HLSL_OR:%.*]] = or i1 [[x]], [[y]]
//CHECK-NEXT: ret i1 [[HLSL_OR]]
//CHECK_NEXT: }
bool test_or_bool(bool x, bool y)
{
return or(x, y);

}

//CHECK-LABEL: define noundef <2 x i1> @_Z13test_or_bool2Dv2_bS_(
//CHECK-SAME: <2 x i1> noundef [[X:%.*]], <2 x i1> noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
//CHECK-NEXT: [[ENTRY:.*:]]
//CHECK-NEXT: [[HLSL_OR:%.*]] = or <2 xi1> [[x]], [[y]]
//CHECK-NEXT: ret <2 x i1> [[HLSL_OR]]
//CHECK_NEXT: }
bool2 test_or_bool2(bool2 x, bool2 y)
{
return or(x, y);
}

//CHECK-LABEL: define noundef <3 x i1> @_Z13test_or_bool3Dv3_bS_(
//CHECK-SAME: <3 x i1> noundef [[X:%.*]], <3 x i1> noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
//CHECK-NEXT: [[ENTRY:.*:]]
//CHECK-NEXT: [[HLSL_OR:%.*]] = or <3 xi1> [[x]], [[y]]
//CHECK-NEXT: ret <3 x i1> [[HLSL_OR]]
//CHECK_NEXT: }
bool3 test_or_bool3(bool3 x, bool3 y)
{
return or(x, y);
}

//CHECK-LABEL: define noundef <4 x i1> @_Z13test_or_bool4Dv4_bS_(
//CHECK-SAME: <4 x i1> noundef [[X:%.*]], <4 x i1> noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
//CHECK-NEXT: [[ENTRY:.*:]]
//CHECK-NEXT: [[HLSL_OR:%.*]] = or <4 xi1> [[x]], [[y]]
//CHECK-NEXT: ret <4 x i1> [[HLSL_OR]]
//CHECK_NEXT: }
bool4 test_or_bool4(bool4 x, bool4 y)
{
return or(x, y);
}

//CHECK-LABEL: define noundef i1 @_Z11test_or_intii(
//CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
//CHECK-NEXT: [[ENTRY:.*:]]
//CHECK_NEXT: [[0:%.*]] = or i32 [[y]], [[x]]
//CHECK-NEXT: [[HLSL_OR:%.*]] = icmp ne i32 [[0]], 0
//CHECK-NEXT: ret i1 [[HLSL_OR]]
//CHECK_NEXT: }
bool test_or_int(int x, int y)
{
return or(x, y);
}

//CHECK-LABEL: define noundef <4 x i1> @_Z12test_or_int4Dv4_iS_(
//CHECK-SAME: <4 x i32> noundef [[X:%.*]], <4 x i32> noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
//CHECK-NEXT: [[ENTRY:.*:]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this line have a stricter match? If not, does it make since to just delete it? .* is such a broad match that it doesn't provide much value imo

//CHECK_NEXT: [[0:%.*]] = or <4 x i32> [[y]], [[x]]
//CHECK-NEXT: [[HLSL_OR:%.*]] = icmp ne <4 x i32> [[0]], zeroinitializer
//CHECK-NEXT: ret <4 x i1> [[HLSL_OR]]
//CHECK_NEXT: }
bool4 test_or_int4(int4 x, int4 y)
{
return or(x, y);
}

//CHECK-LABEL: noundef <4 x i1> @_Z14test_or_float4Dv4_fS_(
//CHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[X:%.*]], <4 x float> noundef nofpclass(nan inf) [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] {
//CHECK-NEXT: [[ENTRY:.*:]]
//CHECK-NEXT: [[TOBOOL:%.*]] = fcmp reassoc nnan ninf nsz arcp afn une <4 x float> [[X]], zeroinitializer
//CHECK-NEXT: [[TOBOOL1:%.*]] = fcmp reassoc nnan ninf nsz arcp afn une <4 x float> [[Y]], zeroinitializer
//CHECK-NEXT: [[HLSL_OR:%.*]] = or <4 x i1> [[TOBOOL]], [[TOBOOL1]]
//CHECK-NEXT: ret <4 x i1> [[HLSL_OR]]
//CHECK_NEXT: }
bool4 test_or_float4(float4 x, float4 y)
{
return or(x, y);
}
22 changes: 0 additions & 22 deletions clang/test/SemaHLSL/BuiltIns/and-errors.hlsl

This file was deleted.

27 changes: 27 additions & 0 deletions clang/test/SemaHLSL/BuiltIns/logical-operator-errors.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -verify -DTEST_FUNC=__builtin_hlsl_or
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -verify -DTEST_FUNC=__builtin_hlsl_and


bool test_too_few_arg(bool a)
{
return TEST_FUNC(a);
// expected-error@-1 {{too few arguments to function call, expected 2, have 1}}
}

bool test_too_many_arg(bool a)
{
return TEST_FUNC(a, a, a);
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}}
}

bool2 test_mismatched_args(bool2 a, bool3 b)
{
return TEST_FUNC(a, b);
// expected-error@-1 {{all arguments to}}{{.*}}{{must have the same type}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suggestion is almost certainly wrong way to type this out. Just trying to show the idea. Does the .* here capture and/or in the error message? If yes, match the possible valid cases with an or regex instead of allowing anything (another point for DAMP being more clear)

Suggested change
// expected-error@-1 {{all arguments to}}{{.*}}{{must have the same type}}
// expected-error@-1 {{all arguments to}}{{and|or}}{{must have the same type}}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should actually be _builtin_hlsl_or and _builtin_hlsl_and but the idea is correct.

}

bool test_incorrect_type(int a)
{
return TEST_FUNC(a, a);
// expected-error@-1{{invalid operand of type 'int' where 'bool' or a vector of such type is required}}
}