Skip to content

[SPIR-V]: Improve pattern matching to recognize a composite constant to be a constant #96286

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 3 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 3 additions & 6 deletions llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -670,10 +670,8 @@ void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) {
AggrConst = cast<Constant>(COp);
ResTy = B.getInt32Ty();
} else if (auto *COp = dyn_cast<ConstantAggregateZero>(Op)) {
if (!Op->getType()->isVectorTy()) {
AggrConst = cast<Constant>(COp);
ResTy = B.getInt32Ty();
}
AggrConst = cast<Constant>(COp);
ResTy = Op->getType()->isVectorTy() ? COp->getType() : B.getInt32Ty();
}
if (AggrConst) {
SmallVector<Value *> Args;
Expand Down Expand Up @@ -1260,8 +1258,7 @@ void SPIRVEmitIntrinsics::processInstrAfterVisit(Instruction *I,
}
bool IsPhi = isa<PHINode>(I), BPrepared = false;
for (const auto &Op : I->operands()) {
if ((isa<ConstantAggregateZero>(Op) && Op->getType()->isVectorTy()) ||
isa<PHINode>(I) || isa<SwitchInst>(I))
if (isa<PHINode>(I) || isa<SwitchInst>(I))
TrackConstants = false;
if ((isa<ConstantData>(Op) || isa<ConstantExpr>(Op)) && TrackConstants) {
unsigned OpNo = Op.getOperandNo();
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,12 @@ class SPIRVGlobalRegistry {
return Ret;
}

// Return true if the type is an aggregate type.
bool isAggregateType(SPIRVType *Type) const {
return Type && (Type->getOpcode() == SPIRV::OpTypeStruct &&
Type->getOpcode() == SPIRV::OpTypeArray);
}

// Whether the given VReg has an OpTypeXXX instruction mapped to it with the
// given opcode (e.g. OpTypeFloat).
bool isScalarOfType(Register VReg, unsigned TypeOpcode) const;
Expand Down
44 changes: 38 additions & 6 deletions llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1445,20 +1445,50 @@ static unsigned getArrayComponentCount(MachineRegisterInfo *MRI,
}

// Return true if the type represents a constant register
static bool isConstReg(MachineRegisterInfo *MRI, SPIRVType *OpDef) {
static bool isConstReg(MachineRegisterInfo *MRI, SPIRVType *OpDef,
SmallPtrSet<SPIRVType *, 4> &Visited) {
if (OpDef->getOpcode() == SPIRV::ASSIGN_TYPE &&
OpDef->getOperand(1).isReg()) {
if (SPIRVType *RefDef = MRI->getVRegDef(OpDef->getOperand(1).getReg()))
OpDef = RefDef;
}
return OpDef->getOpcode() == TargetOpcode::G_CONSTANT ||
OpDef->getOpcode() == TargetOpcode::G_FCONSTANT;

if (Visited.contains(OpDef))
return true;
Visited.insert(OpDef);

unsigned Opcode = OpDef->getOpcode();
switch (Opcode) {
case TargetOpcode::G_CONSTANT:
case TargetOpcode::G_FCONSTANT:
return true;
case TargetOpcode::G_INTRINSIC:
case TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS:
case TargetOpcode::G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS:
return cast<GIntrinsic>(*OpDef).getIntrinsicID() ==
Intrinsic::spv_const_composite;
case TargetOpcode::G_BUILD_VECTOR:
case TargetOpcode::G_SPLAT_VECTOR: {
for (unsigned i = OpDef->getNumExplicitDefs(); i < OpDef->getNumOperands();
i++) {
SPIRVType *OpNestedDef =
OpDef->getOperand(i).isReg()
? MRI->getVRegDef(OpDef->getOperand(i).getReg())
: nullptr;
if (OpNestedDef && !isConstReg(MRI, OpNestedDef, Visited))
return false;
}
return true;
}
}
return false;
}

// Return true if the virtual register represents a constant
static bool isConstReg(MachineRegisterInfo *MRI, Register OpReg) {
SmallPtrSet<SPIRVType *, 4> Visited;
if (SPIRVType *OpDef = MRI->getVRegDef(OpReg))
return isConstReg(MRI, OpDef);
return isConstReg(MRI, OpDef, Visited);
return false;
}

Expand Down Expand Up @@ -1882,8 +1912,10 @@ bool SPIRVInstructionSelector::wrapIntoSpecConstantOp(
Register OpReg = I.getOperand(i).getReg();
SPIRVType *OpDefine = MRI->getVRegDef(OpReg);
SPIRVType *OpType = GR.getSPIRVTypeForVReg(OpReg);
if (!OpDefine || !OpType || isConstReg(MRI, OpDefine) ||
OpDefine->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST) {
SmallPtrSet<SPIRVType *, 4> Visited;
if (!OpDefine || !OpType || isConstReg(MRI, OpDefine, Visited) ||
OpDefine->getOpcode() == TargetOpcode::G_ADDRSPACE_CAST ||
GR.isAggregateType(OpType)) {
// The case of G_ADDRSPACE_CAST inside spv_const_composite() is processed
// by selectAddrSpaceCast()
CompositeArgs.push_back(OpReg);
Expand Down
21 changes: 21 additions & 0 deletions llvm/test/CodeGen/SPIRV/const-array-in-struct.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}

; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}

; CHECK-SPIRV: %[[#IntTy:]] = OpTypeInt 32 0
; CHECK-SPIRV: %[[#Const16:]] = OpConstant %[[#IntTy]] 16
; CHECK-SPIRV: %[[#ArrayTy:]] = OpTypeArray %[[#IntTy]] %[[#Const16]]
; CHECK-SPIRV: %[[#StructTy:]] = OpTypeStruct %[[#ArrayTy]]
; CHECK-SPIRV-COUNT-16: %[[#]] = OpConstant %[[#IntTy]] {{[0-9]+}}
; CHECK-SPIRV: %[[#ConstArray:]] = OpConstantComposite %[[#ArrayTy]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]] %[[#]]
; CHECK-SPIRV: %[[#]] = OpConstantComposite %[[#StructTy]] %[[#ConstArray]]

%struct_array_16i32 = type { [16 x i32] }

@G = private unnamed_addr addrspace(1) constant %struct_array_16i32 { [16 x i32] [i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15] }, align 4

define spir_kernel void @test() {
ret void
}
127 changes: 127 additions & 0 deletions llvm/test/CodeGen/SPIRV/const-nested-vecs.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}

; TODO: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
; TODO: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}

; CHECK-SPIRV: OpName %[[#Var:]] "var"
; CHECK-SPIRV: OpName %[[#GVar:]] "g_var"
; CHECK-SPIRV: OpName %[[#AVar:]] "a_var"
; CHECK-SPIRV: OpName %[[#PVar:]] "p_var"

; CHECK-SPIRV-DAG: %[[#CharTy:]] = OpTypeInt 8 0
; CHECK-SPIRV-DAG: %[[#IntTy:]] = OpTypeInt 32 0
; CHECK-SPIRV-DAG: %[[#V2CharTy:]] = OpTypeVector %[[#CharTy]] 2
; CHECK-SPIRV-DAG: %[[#V2ConstNull:]] = OpConstantNull %[[#V2CharTy]]
; CHECK-SPIRV-DAG: %[[#Const1:]] = OpConstant %[[#CharTy]] 1
; CHECK-SPIRV-DAG: %[[#Const2:]] = OpConstant %[[#IntTy]] 2
; CHECK-SPIRV-DAG: %[[#Arr2V2CharTy:]] = OpTypeArray %[[#V2CharTy]] %[[#Const2]]
; CHECK-SPIRV-DAG: %[[#LongTy:]] = OpTypeInt 64 0
; CHECK-SPIRV-DAG: %[[#PtrV2CharTy:]] = OpTypePointer CrossWorkgroup %[[#V2CharTy]]
; CHECK-SPIRV-DAG: %[[#V2Char1:]] = OpConstantComposite %[[#V2CharTy]] %[[#Const1]] %[[#Const1]]
; CHECK-SPIRV-DAG: %[[#Arr2V2Char:]] = OpConstantComposite %[[#Arr2V2CharTy]] %[[#V2Char1]] %[[#V2Char1]]
; CHECK-SPIRV-DAG: %[[#PtrCharTy:]] = OpTypePointer CrossWorkgroup %[[#CharTy]]
; CHECK-SPIRV-DAG: %[[#PtrArr2V2CharTy:]] = OpTypePointer CrossWorkgroup %[[#Arr2V2CharTy]]
; CHECK-SPIRV-DAG: %[[#IntZero:]] = OpConstantNull %[[#IntTy]]
; CHECK-SPIRV-DAG: %[[#LongZero:]] = OpConstantNull %[[#LongTy]]
; CHECK-SPIRV-DAG: %[[#ConstLong2:]] = OpConstant %[[#LongTy]] 2
; CHECK-SPIRV-DAG: %[[#PvarInit:]] = OpSpecConstantOp %[[#PtrCharTy]] 70 %[[#VarV2Char:]] %[[#IntZero]] %[[#ConstLong2]]
; CHECK-SPIRV-DAG: %[[#PtrPtrCharTy:]] = OpTypePointer CrossWorkgroup %[[#PtrCharTy]]
; CHECK-SPIRV-DAG: %[[#AVar]] = OpVariable %[[#PtrArr2V2CharTy]] CrossWorkgroup %[[#Arr2V2Char]]
; CHECK-SPIRV-DAG: %[[#PVar]] = OpVariable %[[#PtrPtrCharTy]] CrossWorkgroup %[[#PvarInit]]
; CHECK-SPIRV-DAG: %[[#GVar]] = OpVariable %[[#PtrV2CharTy]] CrossWorkgroup %[[#V2Char1]]
; CHECK-SPIRV-DAG: %[[#]] = OpVariable %[[#PtrV2CharTy]] CrossWorkgroup %[[#V2ConstNull]]

; As an option: %[[#Const0:]] = OpConstant %[[#CharTy]] 0
; %[[#V2CharZero:]] = OpConstantComposite %[[#V2CharTy]] %[[#Const0]] %[[#Const0]]
; %[[#]] = OpVariable %[[#PtrV2CharTy]] CrossWorkgroup %[[#V2CharZero]]

@var = addrspace(1) global <2 x i8> zeroinitializer, align 2
@g_var = addrspace(1) global <2 x i8> <i8 1, i8 1>, align 2
@a_var = addrspace(1) global [2 x <2 x i8>] [<2 x i8> <i8 1, i8 1>, <2 x i8> <i8 1, i8 1>], align 2
@p_var = addrspace(1) global ptr addrspace(1) getelementptr (i8, ptr addrspace(1) @a_var, i64 2), align 8

define spir_func <2 x i8> @from_buf(<2 x i8> %a) #0 {
entry:
ret <2 x i8> %a
}

define spir_func <2 x i8> @to_buf(<2 x i8> %a) #0 {
entry:
ret <2 x i8> %a
}

define spir_kernel void @writer(ptr addrspace(1) %src, i32 %idx) #0 !kernel_arg_addr_space !5 !kernel_arg_access_qual !6 !kernel_arg_type !7 !kernel_arg_type_qual !8 !kernel_arg_base_type !7 !spirv.ParameterDecorations !9 {
entry:
%arrayidx = getelementptr inbounds <2 x i8>, ptr addrspace(1) %src, i64 0
%0 = load <2 x i8>, ptr addrspace(1) %arrayidx, align 2
%call = call spir_func <2 x i8> @from_buf(<2 x i8> %0) #0
store <2 x i8> %call, ptr addrspace(1) @var, align 2
%arrayidx1 = getelementptr inbounds <2 x i8>, ptr addrspace(1) %src, i64 1
%1 = load <2 x i8>, ptr addrspace(1) %arrayidx1, align 2
%call2 = call spir_func <2 x i8> @from_buf(<2 x i8> %1) #0
store <2 x i8> %call2, ptr addrspace(1) @g_var, align 2
%arrayidx3 = getelementptr inbounds <2 x i8>, ptr addrspace(1) %src, i64 2
%2 = load <2 x i8>, ptr addrspace(1) %arrayidx3, align 2
%call4 = call spir_func <2 x i8> @from_buf(<2 x i8> %2) #0
%3 = getelementptr inbounds [2 x <2 x i8>], ptr addrspace(1) @a_var, i64 0, i64 0
store <2 x i8> %call4, ptr addrspace(1) %3, align 2
%arrayidx5 = getelementptr inbounds <2 x i8>, ptr addrspace(1) %src, i64 3
%4 = load <2 x i8>, ptr addrspace(1) %arrayidx5, align 2
%call6 = call spir_func <2 x i8> @from_buf(<2 x i8> %4) #0
%5 = getelementptr inbounds [2 x <2 x i8>], ptr addrspace(1) @a_var, i64 0, i64 1
store <2 x i8> %call6, ptr addrspace(1) %5, align 2
%idx.ext = zext i32 %idx to i64
%add.ptr = getelementptr inbounds <2 x i8>, ptr addrspace(1) %3, i64 %idx.ext
store ptr addrspace(1) %add.ptr, ptr addrspace(1) @p_var, align 8
ret void
}

define spir_kernel void @reader(ptr addrspace(1) %dest, <2 x i8> %ptr_write_val) #0 !kernel_arg_addr_space !5 !kernel_arg_access_qual !6 !kernel_arg_type !10 !kernel_arg_type_qual !8 !kernel_arg_base_type !10 !spirv.ParameterDecorations !9 {
entry:
%call = call spir_func <2 x i8> @from_buf(<2 x i8> %ptr_write_val) #0
%0 = load ptr addrspace(1), ptr addrspace(1) @p_var, align 8
store volatile <2 x i8> %call, ptr addrspace(1) %0, align 2
%1 = load <2 x i8>, ptr addrspace(1) @var, align 2
%call1 = call spir_func <2 x i8> @to_buf(<2 x i8> %1) #0
%arrayidx = getelementptr inbounds <2 x i8>, ptr addrspace(1) %dest, i64 0
store <2 x i8> %call1, ptr addrspace(1) %arrayidx, align 2
%2 = load <2 x i8>, ptr addrspace(1) @g_var, align 2
%call2 = call spir_func <2 x i8> @to_buf(<2 x i8> %2) #0
%arrayidx3 = getelementptr inbounds <2 x i8>, ptr addrspace(1) %dest, i64 1
store <2 x i8> %call2, ptr addrspace(1) %arrayidx3, align 2
%3 = getelementptr inbounds [2 x <2 x i8>], ptr addrspace(1) @a_var, i64 0, i64 0
%4 = load <2 x i8>, ptr addrspace(1) %3, align 2
%call4 = call spir_func <2 x i8> @to_buf(<2 x i8> %4) #0
%arrayidx5 = getelementptr inbounds <2 x i8>, ptr addrspace(1) %dest, i64 2
store <2 x i8> %call4, ptr addrspace(1) %arrayidx5, align 2
%5 = getelementptr inbounds [2 x <2 x i8>], ptr addrspace(1) @a_var, i64 0, i64 1
%6 = load <2 x i8>, ptr addrspace(1) %5, align 2
%call6 = call spir_func <2 x i8> @to_buf(<2 x i8> %6) #0
%arrayidx7 = getelementptr inbounds <2 x i8>, ptr addrspace(1) %dest, i64 3
store <2 x i8> %call6, ptr addrspace(1) %arrayidx7, align 2
ret void
}

attributes #0 = { nounwind }

!spirv.MemoryModel = !{!0}
!opencl.enable.FP_CONTRACT = !{}
!spirv.Source = !{!1}
!opencl.spir.version = !{!2}
!opencl.ocl.version = !{!2}
!opencl.used.extensions = !{!3}
!opencl.used.optional.core.features = !{!3}
!spirv.Generator = !{!4}

!0 = !{i32 2, i32 2}
!1 = !{i32 3, i32 200000}
!2 = !{i32 2, i32 0}
!3 = !{}
!4 = !{i16 6, i16 14}
!5 = !{i32 1, i32 0}
!6 = !{!"none", !"none"}
!7 = !{!"char2*", !"int"}
!8 = !{!"", !""}
!9 = !{!3, !3}
!10 = !{!"char2*", !"char2"}
4 changes: 4 additions & 0 deletions llvm/test/CodeGen/SPIRV/opencl/image.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}

; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}

;; FIXME: Write tests to ensure invalid usage of image are rejected, such as:
;; - invalid AS (only global is allowed);
Expand Down
25 changes: 25 additions & 0 deletions llvm/test/CodeGen/SPIRV/pointers/global-zeroinitializer.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}

; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv32-unknown-unknown %s -o - -filetype=obj | spirv-val %}

; CHECK: OpName %[[#Var:]] "var"
; CHECK-DAG: %[[#Char:]] = OpTypeInt 8 0
; CHECK-DAG: %[[#Vec2Char:]] = OpTypeVector %[[#Char]] 2
; CHECK-DAG: %[[#PtrVec2Char:]] = OpTypePointer CrossWorkgroup %[[#Vec2Char]]
; CHECK-DAG: %[[#ConstNull:]] = OpConstantNull %[[#Vec2Char]]
; CHECK: %[[#]] = OpVariable %[[#PtrVec2Char]] CrossWorkgroup %[[#ConstNull]]
; As an option: %[[#C0:]] = OpConstant %[[#Char]] 0
; %[[#VecZero:]] = OpConstantComposite %[[#Vec2Char]] %[[#C0]] %[[#C0]]
; %[[#]] = OpVariable %[[#PtrVec2Char]] CrossWorkgroup %[[#VecZero]]
; CHECK: OpFunction

@var = addrspace(1) global <2 x i8> zeroinitializer
;@var = addrspace(1) global <2 x i8> <i8 1, i8 1>

define spir_kernel void @foo() {
entry:
%addr = load <2 x i8>, ptr addrspace(1) @var, align 2
ret void
}
Loading