-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[HLSL] Support vector swizzles on scalars #67700
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \ | ||
// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \ | ||
// RUN: -o - | FileCheck %s | ||
|
||
// CHECK-LABEL: ToTwoInts | ||
// CHECK: [[splat:%.*]] = insertelement <1 x i32> poison, i32 {{.*}}, i64 0 | ||
// CHECK: [[vec2:%.*]] = shufflevector <1 x i32> [[splat]], <1 x i32> poison, <2 x i32> zeroinitializer | ||
// CHECK: ret <2 x i32> [[vec2]] | ||
int2 ToTwoInts(int V){ | ||
return V.xx; | ||
} | ||
|
||
// CHECK-LABEL: ToFourFloats | ||
// [[splat:%.*]] = insertelement <1 x float> poison, float {{.*}}, i64 0 | ||
// [[vec4:%.*]] = shufflevector <1 x float> [[splat]], <1 x float> poison, <4 x i32> zeroinitializer | ||
// ret <4 x float> [[vec4]] | ||
float4 ToFourFloats(float V){ | ||
return V.rrrr; | ||
} | ||
|
||
// CHECK-LABEL: FillOne | ||
// CHECK: [[vec1Ptr:%.*]] = alloca <1 x i32>, align 4 | ||
// CHECK: store <1 x i32> <i32 1>, ptr [[vec1Ptr]], align 4 | ||
// CHECK: [[vec1:%.*]] = load <1 x i32>, ptr [[vec1Ptr]], align 4 | ||
// CHECK: [[vec2:%.*]] = shufflevector <1 x i32> [[vec1]], <1 x i32> poison, <2 x i32> zeroinitializer | ||
// CHECK: ret <2 x i32> [[vec2]] | ||
int2 FillOne(){ | ||
return 1.xx; | ||
} | ||
|
||
// CHECK-LABEL: FillOneUnsigned | ||
// CHECK: [[vec1Ptr:%.*]] = alloca <1 x i32>, align 4 | ||
// CHECK: store <1 x i32> <i32 1>, ptr [[vec1Ptr]], align 4 | ||
// CHECK: [[vec1:%.*]] = load <1 x i32>, ptr [[vec1Ptr]], align 4 | ||
// CHECK: [[vec3:%.*]] = shufflevector <1 x i32> [[vec1]], <1 x i32> poison, <3 x i32> zeroinitializer | ||
// CHECK: ret <3 x i32> [[vec3]] | ||
uint3 FillOneUnsigned(){ | ||
return 1u.xxx; | ||
} | ||
|
||
// CHECK-LABEL: FillOneUnsignedLong | ||
// CHECK: [[vec1Ptr:%.*]] = alloca <1 x i64>, align 8 | ||
// CHECK: store <1 x i64> <i64 1>, ptr [[vec1Ptr]], align 8 | ||
// CHECK: [[vec1:%.*]] = load <1 x i64>, ptr [[vec1Ptr]], align 8 | ||
// CHECK: [[vec4:%.*]] = shufflevector <1 x i64> [[vec1]], <1 x i64> poison, <4 x i32> zeroinitializer | ||
// CHECK: ret <4 x i64> [[vec4]] | ||
vector<uint64_t,4> FillOneUnsignedLong(){ | ||
return 1ul.xxxx; | ||
} | ||
|
||
// CHECK-LABEL: FillTwoPointFive | ||
// CHECK: [[vec1Ptr:%.*]] = alloca <1 x double>, align 8 | ||
// CHECK: store <1 x double> <double 2.500000e+00>, ptr [[vec1Ptr]], align 8 | ||
// CHECK: [[vec1:%.*]] = load <1 x double>, ptr [[vec1Ptr]], align 8 | ||
// CHECK: [[vec2:%.*]] = shufflevector <1 x double> [[vec1]], <1 x double> poison, <2 x i32> zeroinitializer | ||
// CHECK: ret <2 x double> [[vec2]] | ||
double2 FillTwoPointFive(){ | ||
return 2.5.rr; | ||
} | ||
|
||
// CHECK-LABEL: FillOneHalf | ||
// CHECK: [[vec1Ptr:%.*]] = alloca <1 x double>, align 8 | ||
// CHECK: store <1 x double> <double 5.000000e-01>, ptr [[vec1Ptr]], align 8 | ||
// CHECK: [[vec1:%.*]] = load <1 x double>, ptr [[vec1Ptr]], align 8 | ||
// CHECK: [[vec3:%.*]] = shufflevector <1 x double> [[vec1]], <1 x double> poison, <3 x i32> zeroinitializer | ||
// CHECK: ret <3 x double> [[vec3]] | ||
double3 FillOneHalf(){ | ||
return .5.rrr; | ||
} | ||
|
||
// CHECK-LABEL: FillTwoPointFiveFloat | ||
// CHECK: [[vec1Ptr:%.*]] = alloca <1 x float>, align 4 | ||
// CHECK: store <1 x float> <float 2.500000e+00>, ptr [[vec1Ptr]], align 4 | ||
// CHECK: [[vec1:%.*]] = load <1 x float>, ptr [[vec1Ptr]], align 4 | ||
// CHECK: [[vec4:%.*]] = shufflevector <1 x float> [[vec1]], <1 x float> poison, <4 x i32> zeroinitializer | ||
// CHECK: ret <4 x float> [[vec4]] | ||
float4 FillTwoPointFiveFloat(){ | ||
return 2.5f.rrrr; | ||
} | ||
|
||
// The initial codegen for this case is correct but a bit odd. The IR optimizer | ||
// cleans this up very nicely. | ||
|
||
// CHECK-LABEL: FillOneHalfFloat | ||
// CHECK: [[vec1Ptr:%.*]] = alloca <1 x float>, align 4 | ||
// CHECK: store <1 x float> <float 5.000000e-01>, ptr [[vec1Ptr]], align 4 | ||
// CHECK: [[vec1:%.*]] = load <1 x float>, ptr [[vec1Ptr]], align 4 | ||
// CHECK: [[vec1Ret:%.*]] = shufflevector <1 x float> [[vec1]], <1 x float> undef, <1 x i32> zeroinitializer | ||
// CHECK: ret <1 x float> [[vec1Ret]] | ||
vector<float, 1> FillOneHalfFloat(){ | ||
return .5f.r; | ||
} | ||
|
||
// The initial codegen for this case is correct but a bit odd. The IR optimizer | ||
// cleans this up very nicely. | ||
|
||
// CHECK-LABEL: HowManyFloats | ||
// CHECK: [[VAddr:%.*]] = alloca float, align 4 | ||
// CHECK: [[vec2Ptr:%.*]] = alloca <2 x float>, align 8 | ||
// CHECK: [[VVal:%.*]] = load float, ptr [[VAddr]], align 4 | ||
// CHECK: [[splat:%.*]] = insertelement <1 x float> poison, float [[VVal]], i64 0 | ||
// CHECK: [[vec2:%.*]] = shufflevector <1 x float> [[splat]], <1 x float> poison, <2 x i32> zeroinitializer | ||
// CHECK: store <2 x float> [[vec2]], ptr [[vec2Ptr]], align 8 | ||
// CHECK: [[vec2:%.*]] = load <2 x float>, ptr [[vec2Ptr]], align 8 | ||
// CHECK: [[vec2Res:%.*]] = shufflevector <2 x float> [[vec2]], <2 x float> poison, <2 x i32> zeroinitializer | ||
// CHECK: ret <2 x float> [[vec2Res]] | ||
float2 HowManyFloats(float V) { | ||
return V.rr.rr; | ||
} | ||
|
||
// This codegen is gnarly because `1.` is a double, so this creates double | ||
// vectors that need to be truncated down to floats. The optimizer cleans this | ||
// up nicely too. | ||
|
||
// CHECK-LABEL: AllRighty | ||
// CHECK: [[XTmp:%.*]] = alloca <1 x double>, align 8 | ||
// CHECK: [[YTmp:%.*]] = alloca <1 x double>, align 8 | ||
// CHECK: [[ZTmp:%.*]] = alloca <1 x double>, align 8 | ||
|
||
// CHECK: store <1 x double> <double 1.000000e+00>, ptr [[XTmp]], align 8 | ||
// CHECK: [[XVec:%.*]] = load <1 x double>, ptr [[XTmp]], align 8 | ||
// CHECK: [[XVec3:%.*]] = shufflevector <1 x double> [[XVec]], <1 x double> poison, <3 x i32> zeroinitializer | ||
// CHECK: [[XVal:%.*]] = extractelement <3 x double> [[XVec3]], i32 0 | ||
// CHECK: [[XValF:%.*]] = fptrunc double [[XVal]] to float | ||
// CHECK: [[Vec3F1:%.*]] = insertelement <3 x float> undef, float [[XValF]], i32 0 | ||
|
||
// CHECK: store <1 x double> <double 1.000000e+00>, ptr [[YTmp]], align 8 | ||
// CHECK: [[YVec:%.*]] = load <1 x double>, ptr [[YTmp]], align 8 | ||
// CHECK: [[YVec3:%.*]] = shufflevector <1 x double> [[YVec]], <1 x double> poison, <3 x i32> zeroinitializer | ||
// CHECK: [[YVal:%.*]] = extractelement <3 x double> [[YVec3]], i32 1 | ||
// CHECK: [[YValF:%.*]] = fptrunc double [[YVal]] to float | ||
// CHECK: [[Vec3F2:%.*]] = insertelement <3 x float> [[Vec3F1]], float [[YValF]], i32 1 | ||
|
||
// CHECK: store <1 x double> <double 1.000000e+00>, ptr [[ZTmp]], align 8 | ||
// CHECK: [[ZVec:%.*]] = load <1 x double>, ptr [[ZTmp]], align 8 | ||
// CHECK: [[ZVec3:%.*]] = shufflevector <1 x double> [[ZVec]], <1 x double> poison, <3 x i32> zeroinitializer | ||
// CHECK: [[ZVal:%.*]] = extractelement <3 x double> [[ZVec3]], i32 2 | ||
// CHECK: [[ZValF:%.*]] = fptrunc double [[ZVal]] to float | ||
// CHECK: [[Vec3F3:%.*]] = insertelement <3 x float> [[Vec3F2]], float [[ZValF]], i32 2 | ||
|
||
// ret <3 x float> [[Vec3F3]] | ||
float3 AllRighty() { | ||
return 1..rrr; | ||
} | ||
|
||
// CHECK-LABEL: AssignInt | ||
// CHECK: [[VAddr:%.*]] = alloca i32, align 4 | ||
// CHECK: [[XAddr:%.*]] = alloca i32, align 4 | ||
|
||
// Load V into a vector, then extract V out and store it to X. | ||
// CHECK: [[V:%.*]] = load i32, ptr [[VAddr]], align 4 | ||
// CHECK: [[Splat:%.*]] = insertelement <1 x i32> poison, i32 [[V]], i64 0 | ||
// CHECK: [[VExtVal:%.*]] = extractelement <1 x i32> [[Splat]], i32 0 | ||
// CHECK: store i32 [[VExtVal]], ptr [[XAddr]], align 4 | ||
|
||
// Load V into two separate vectors, then add the extracted X components. | ||
// CHECK: [[V:%.*]] = load i32, ptr [[VAddr]], align 4 | ||
// CHECK: [[Splat:%.*]] = insertelement <1 x i32> poison, i32 [[V]], i64 0 | ||
// CHECK: [[LHS:%.*]] = extractelement <1 x i32> [[Splat]], i32 0 | ||
|
||
// CHECK: [[V:%.*]] = load i32, ptr [[VAddr]], align 4 | ||
// CHECK: [[Splat:%.*]] = insertelement <1 x i32> poison, i32 [[V]], i64 0 | ||
// CHECK: [[RHS:%.*]] = extractelement <1 x i32> [[Splat]], i32 0 | ||
|
||
// CHECK: [[Sum:%.*]] = add nsw i32 [[LHS]], [[RHS]] | ||
// CHECK: store i32 [[Sum]], ptr [[XAddr]], align 4 | ||
// CHECK: [[X:%.*]] = load i32, ptr [[XAddr]], align 4 | ||
// CHECK: ret i32 [[X]] | ||
|
||
int AssignInt(int V){ | ||
int X = V.x; | ||
X.x = V.x + V.x; | ||
return X; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -x hlsl -finclude-default-header -verify %s | ||
|
||
int2 ToTwoInts(int V) { | ||
return V.xy; // expected-error{{vector component access exceeds type 'int __attribute__((ext_vector_type(1)))' (vector of 1 'int' value)}} | ||
} | ||
|
||
float2 ToTwoFloats(float V) { | ||
return V.rg; // expected-error{{vector component access exceeds type 'float __attribute__((ext_vector_type(1)))' (vector of 1 'float' value)}} | ||
} | ||
|
||
int4 SomeNonsense(int V) { | ||
return V.poop; // expected-error{{illegal vector component name 'p'}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like another test that validates we catch multiple levels of dots. e.g.,
or dots not followed by anything useful:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The really sad thing is that the first case there seems to be valid on both our reference compilers: DXC on Compiler Explorer We may need to support that. I'll add tests either way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please also add CodeGen tests that show we do... whatever that is. |
||
} | ||
|
||
float2 WhatIsHappening(float V) { | ||
return V.; // expected-error{{expected unqualified-id}} | ||
} | ||
|
||
// These cases produce no error. | ||
|
||
float2 HowManyFloats(float V) { | ||
return V.rr.rr; | ||
} | ||
|
||
int64_t4 HooBoy() { | ||
return 4l.xxxx; | ||
} | ||
|
||
float3 AllRighty() { | ||
return 1..rrr; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.