diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 3e0e9bb5e39e5..dca9d6e7ea358 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -15944,6 +15944,20 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) << resultType << Input.get()->getSourceRange()); } + } else if (Context.getLangOpts().HLSL && resultType->isVectorType() && + !resultType->hasBooleanRepresentation()) { + // HLSL unary logical 'not' behaves like C++, which states that the + // operand is converted to bool and the result is bool, however HLSL + // extends this property to vectors. + const VectorType *VTy = resultType->castAs(); + resultType = + Context.getExtVectorType(Context.BoolTy, VTy->getNumElements()); + + Input = ImpCastExprToType( + Input.get(), resultType, + ScalarTypeToBooleanCastKind(VTy->getElementType())) + .get(); + break; } else if (resultType->isExtVectorType()) { if (Context.getLangOpts().OpenCL && Context.getLangOpts().getOpenCLCompatibleVersion() < 120) { diff --git a/clang/test/CodeGenHLSL/Operators/logical-not.hlsl b/clang/test/CodeGenHLSL/Operators/logical-not.hlsl new file mode 100644 index 0000000000000..0f9d0677d8610 --- /dev/null +++ b/clang/test/CodeGenHLSL/Operators/logical-not.hlsl @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -disable-llvm-passes -emit-llvm -finclude-default-header -fnative-half-type -o - %s | FileCheck %s + +// CHECK-LABEL: case1 +// CHECK: [[ToBool:%.*]] = icmp ne <2 x i32> {{.*}}, zeroinitializer +// CHECK-NEXT: [[BoolCmp:%.*]] = icmp eq <2 x i1> [[ToBool]], zeroinitializer +// CHECK-NEXT: {{.*}} = zext <2 x i1> [[BoolCmp]] to <2 x i32> +export uint32_t2 case1(uint32_t2 b) { + return !b; +} + +// CHECK-LABEL: case2 +// CHECK: [[ToBool:%.*]] = icmp ne <3 x i32> {{.*}}, zeroinitializer +// CHECK-NEXT: [[BoolCmp:%.*]] = icmp eq <3 x i1> [[ToBool]], zeroinitializer +// CHECK-NEXT: {{.*}} = zext <3 x i1> [[BoolCmp]] to <3 x i32> +export int32_t3 case2(int32_t3 b) { + return !b; +} + +// CHECK-LABEL: case3 +// CHECK: [[ToBool:%.*]] = fcmp reassoc nnan ninf nsz arcp afn une half {{.*}}, 0xH0000 +// CHECK-NEXT: [[BoolCmp:%.*]] = xor i1 [[ToBool]], true +// CHECK-NEXT: {{.*}} = uitofp i1 [[BoolCmp]] to half +export float16_t case3(float16_t b) { + return !b; +} + +// CHECK-LABEL: case4 +// CHECK: [[ToBool:%.*]] = fcmp reassoc nnan ninf nsz arcp afn une <4 x float> {{.*}}, zeroinitializer +// CHECK-NEXT: [[BoolCmp:%.*]] = icmp eq <4 x i1> [[ToBool]], zeroinitializer +// CHECK-NEXT: {{.*}} = uitofp <4 x i1> [[BoolCmp]] to <4 x float> +export float4 case4(float4 b) { + return !b; +} diff --git a/clang/test/SemaHLSL/Operators/logical-not.hlsl b/clang/test/SemaHLSL/Operators/logical-not.hlsl new file mode 100644 index 0000000000000..d06ca3982be05 --- /dev/null +++ b/clang/test/SemaHLSL/Operators/logical-not.hlsl @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -ast-dump -ast-dump-filter=case | FileCheck %s + +// CHECK-LABEL: FunctionDecl {{.*}} used case1 'uint32_t2 (uint32_t2)' +// CHECK-NEXT: ParmVarDecl {{.*}} used b 'uint32_t2':'vector' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' +// CHECK-NEXT: UnaryOperator {{.*}} 'vector' prefix '!' cannot overflow +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'uint32_t2':'vector' +// CHECK-NEXT: DeclRefExpr {{.*}} 'uint32_t2':'vector' lvalue ParmVar {{.*}} 'b' 'uint32_t2':'vector' +export uint32_t2 case1(uint32_t2 b) { + return !b; +} + +// CHECK-LABEL: FunctionDecl {{.*}} used case2 'int32_t3 (int32_t3)' +// CHECK-NEXT: ParmVarDecl {{.*}} used b 'int32_t3':'vector' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' +// CHECK-NEXT: UnaryOperator {{.*}} 'vector' prefix '!' cannot overflow +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int32_t3':'vector' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int32_t3':'vector' lvalue ParmVar {{.*}} 'b' 'int32_t3':'vector' +export int32_t3 case2(int32_t3 b) { + return !b; +} + +// CHECK-LABEL: FunctionDecl {{.*}} used case3 'float16_t (float16_t)' +// CHECK-NEXT: ParmVarDecl {{.*}} used b 'float16_t':'half' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float16_t':'half' +// CHECK-NEXT: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float16_t':'half' +// CHECK-NEXT: DeclRefExpr {{.*}} 'float16_t':'half' lvalue ParmVar {{.*}} 'b' 'float16_t':'half' +export float16_t case3(float16_t b) { + return !b; +} + +// CHECK-LABEL: FunctionDecl {{.*}} used case4 'float4 (float4)' +// CHECK-NEXT: ParmVarDecl {{.*}} used b 'float4':'vector' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' +// CHECK-NEXT: UnaryOperator {{.*}} 'vector' prefix '!' cannot overflow +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'vector' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float4':'vector' +// CHECK-NEXT: DeclRefExpr {{.*}} 'float4':'vector' lvalue ParmVar {{.*}} 'b' 'float4':'vector' +export float4 case4(float4 b) { + return !b; +}