Skip to content

Commit

Permalink
improve Tablegen instruction selection; account for a pointer size of…
Browse files Browse the repository at this point in the history
… the target
  • Loading branch information
VyacheslavLevytskyy committed Apr 15, 2024
1 parent f7bf11c commit 5fdd419
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 74 deletions.
3 changes: 2 additions & 1 deletion llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,8 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
auto MRI = MIRBuilder.getMRI();
assert(MRI->getType(ResVReg).isPointer() && "Pointer type is expected");
if (Reg != ResVReg) {
LLT RegLLTy = LLT::pointer(MRI->getType(ResVReg).getAddressSpace(), 32);
LLT RegLLTy =
LLT::pointer(MRI->getType(ResVReg).getAddressSpace(), getPointerSize());
MRI->setType(Reg, RegLLTy);
assignSPIRVTypeToVReg(BaseType, Reg, MIRBuilder.getMF());
} else {
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,10 @@ void SPIRVInstrInfo::copyPhysReg(MachineBasicBlock &MBB,

bool SPIRVInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
if (MI.getOpcode() == SPIRV::GET_ID || MI.getOpcode() == SPIRV::GET_fID ||
MI.getOpcode() == SPIRV::GET_pID || MI.getOpcode() == SPIRV::GET_vfID ||
MI.getOpcode() == SPIRV::GET_vID || MI.getOpcode() == SPIRV::GET_vpID) {
MI.getOpcode() == SPIRV::GET_pID32 ||
MI.getOpcode() == SPIRV::GET_pID64 || MI.getOpcode() == SPIRV::GET_vfID ||
MI.getOpcode() == SPIRV::GET_vID || MI.getOpcode() == SPIRV::GET_vpID32 ||
MI.getOpcode() == SPIRV::GET_vpID64) {
auto &MRI = MI.getMF()->getRegInfo();
MRI.replaceRegWith(MI.getOperand(0).getReg(), MI.getOperand(1).getReg());
MI.eraseFromParent();
Expand Down
18 changes: 12 additions & 6 deletions llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ let isCodeGenOnly=1 in {
def DECL_TYPE: Pseudo<(outs ANYID:$dst_id), (ins ANYID:$src_id, TYPE:$src_ty)>;
def GET_ID: Pseudo<(outs ID:$dst_id), (ins ANYID:$src)>;
def GET_fID: Pseudo<(outs fID:$dst_id), (ins ANYID:$src)>;
def GET_pID: Pseudo<(outs pID:$dst_id), (ins ANYID:$src)>;
def GET_pID32: Pseudo<(outs pID32:$dst_id), (ins ANYID:$src)>;
def GET_pID64: Pseudo<(outs pID64:$dst_id), (ins ANYID:$src)>;
def GET_vID: Pseudo<(outs vID:$dst_id), (ins ANYID:$src)>;
def GET_vfID: Pseudo<(outs vfID:$dst_id), (ins ANYID:$src)>;
def GET_vpID: Pseudo<(outs vpID:$dst_id), (ins ANYID:$src)>;
def GET_vpID32: Pseudo<(outs vpID32:$dst_id), (ins ANYID:$src)>;
def GET_vpID64: Pseudo<(outs vpID64:$dst_id), (ins ANYID:$src)>;
}

def SPVTypeBin : SDTypeProfile<1, 2, []>;
Expand Down Expand Up @@ -66,8 +68,10 @@ multiclass TernOpTypedGen<string name, bits<16> opCode, SDNode node, bit genP =
def SIVCond: TernOpTyped<name, opCode, vID, ID, node>;
}
if genP then {
def SPSCond: TernOpTyped<name, opCode, ID, pID, node>;
def SPVCond: TernOpTyped<name, opCode, vID, pID, node>;
def SPSCond32: TernOpTyped<name, opCode, ID, pID32, node>;
def SPVCond32: TernOpTyped<name, opCode, vID, pID32, node>;
def SPSCond64: TernOpTyped<name, opCode, ID, pID64, node>;
def SPVCond64: TernOpTyped<name, opCode, vID, pID64, node>;
}
if genV then {
if genF then {
Expand All @@ -79,8 +83,10 @@ multiclass TernOpTypedGen<string name, bits<16> opCode, SDNode node, bit genP =
def VIVCond: TernOpTyped<name, opCode, vID, vID, node>;
}
if genP then {
def VPSCond: TernOpTyped<name, opCode, ID, vpID, node>;
def VPVCond: TernOpTyped<name, opCode, vID, vpID, node>;
def VPSCond32: TernOpTyped<name, opCode, ID, vpID32, node>;
def VPVCond32: TernOpTyped<name, opCode, vID, vpID32, node>;
def VPSCond64: TernOpTyped<name, opCode, ID, vpID64, node>;
def VPVCond64: TernOpTyped<name, opCode, vID, vpID64, node>;
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,14 +284,18 @@ bool SPIRVInstructionSelector::select(MachineInstr &I) {
// If it's not a GMIR instruction, we've selected it already.
if (!isPreISelGenericOpcode(Opcode)) {
if (Opcode == SPIRV::ASSIGN_TYPE) { // These pseudos aren't needed any more.
auto *Def = MRI->getVRegDef(I.getOperand(1).getReg());
Register DstReg = I.getOperand(0).getReg();
Register SrcReg = I.getOperand(1).getReg();
auto *Def = MRI->getVRegDef(SrcReg);
if (isTypeFoldingSupported(Def->getOpcode())) {
if (MRI->getType(DstReg).isPointer())
MRI->setType(DstReg, LLT::scalar(32));
bool Res = selectImpl(I, *CoverageInfo);
assert(Res || Def->getOpcode() == TargetOpcode::G_CONSTANT);
if (Res)
return Res;
}
MRI->replaceRegWith(I.getOperand(1).getReg(), I.getOperand(0).getReg());
MRI->replaceRegWith(SrcReg, DstReg);
I.removeFromParent();
return true;
} else if (I.getNumDefs() == 1) {
Expand Down
5 changes: 3 additions & 2 deletions llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ extern void processInstr(MachineInstr &MI, MachineIRBuilder &MIB,

static bool isMetaInstrGET(unsigned Opcode) {
return Opcode == SPIRV::GET_ID || Opcode == SPIRV::GET_fID ||
Opcode == SPIRV::GET_pID || Opcode == SPIRV::GET_vID ||
Opcode == SPIRV::GET_vfID || Opcode == SPIRV::GET_vpID;
Opcode == SPIRV::GET_pID32 || Opcode == SPIRV::GET_pID64 ||
Opcode == SPIRV::GET_vID || Opcode == SPIRV::GET_vfID ||
Opcode == SPIRV::GET_vpID32 || Opcode == SPIRV::GET_vpID64;
}

static bool mayBeInserted(unsigned Opcode) {
Expand Down
62 changes: 45 additions & 17 deletions llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,11 +223,12 @@ static SPIRVType *propagateSPIRVType(MachineInstr *MI, SPIRVGlobalRegistry *GR,
}

static std::pair<Register, unsigned>
createNewIdReg(Register ValReg, unsigned Opcode, MachineRegisterInfo &MRI,
createNewIdReg(SPIRVType *SpvType, Register SrcReg, MachineRegisterInfo &MRI,
const SPIRVGlobalRegistry &GR) {
LLT NewT = LLT::scalar(32);
SPIRVType *SpvType = GR.getSPIRVTypeForVReg(ValReg);
if (!SpvType)
SpvType = GR.getSPIRVTypeForVReg(SrcReg);
assert(SpvType && "VReg is expected to have SPIRV type");
LLT NewT = LLT::scalar(32);
bool IsFloat = SpvType->getOpcode() == SPIRV::OpTypeFloat;
bool IsVectorFloat =
SpvType->getOpcode() == SPIRV::OpTypeVector &&
Expand All @@ -236,14 +237,38 @@ createNewIdReg(Register ValReg, unsigned Opcode, MachineRegisterInfo &MRI,
IsFloat |= IsVectorFloat;
auto GetIdOp = IsFloat ? SPIRV::GET_fID : SPIRV::GET_ID;
auto DstClass = IsFloat ? &SPIRV::fIDRegClass : &SPIRV::IDRegClass;
if (MRI.getType(ValReg).isPointer()) {
NewT = LLT::pointer(0, 32);
GetIdOp = SPIRV::GET_pID;
DstClass = &SPIRV::pIDRegClass;
} else if (MRI.getType(ValReg).isVector()) {
if (MRI.getType(SrcReg).isPointer()) {
unsigned PtrSz = GR.getPointerSize();
NewT = LLT::pointer(0, PtrSz);
bool IsVec = MRI.getType(SrcReg).isVector();
if (IsVec)
NewT = LLT::fixed_vector(2, NewT);
if (PtrSz == 64) {
if (IsVec) {
GetIdOp = SPIRV::GET_vpID64;
DstClass = &SPIRV::vpID64RegClass;
} else {
GetIdOp = SPIRV::GET_pID64;
DstClass = &SPIRV::pID64RegClass;
}
} else {
if (IsVec) {
GetIdOp = SPIRV::GET_vpID32;
DstClass = &SPIRV::vpID32RegClass;
} else {
GetIdOp = SPIRV::GET_pID32;
DstClass = &SPIRV::pID32RegClass;
}
}
} else if (MRI.getType(SrcReg).isVector()) {
NewT = LLT::fixed_vector(2, NewT);
GetIdOp = IsFloat ? SPIRV::GET_vfID : SPIRV::GET_vID;
DstClass = IsFloat ? &SPIRV::vfIDRegClass : &SPIRV::vIDRegClass;
if (IsFloat) {
GetIdOp = SPIRV::GET_vfID;
DstClass = &SPIRV::vfIDRegClass;
} else {
GetIdOp = SPIRV::GET_vID;
DstClass = &SPIRV::vIDRegClass;
}
}
Register IdReg = MRI.createGenericVirtualRegister(NewT);
MRI.setRegClass(IdReg, DstClass);
Expand All @@ -264,14 +289,14 @@ Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,
MIB.setInsertPt(*Def->getParent(),
(Def->getNextNode() ? Def->getNextNode()->getIterator()
: Def->getParent()->end()));
SpirvTy = SpirvTy ? SpirvTy : GR->getOrCreateSPIRVType(Ty, MIB);
Register NewReg = MRI.createGenericVirtualRegister(MRI.getType(Reg));
if (auto *RC = MRI.getRegClassOrNull(Reg)) {
MRI.setRegClass(NewReg, RC);
} else {
MRI.setRegClass(NewReg, &SPIRV::IDRegClass);
MRI.setRegClass(Reg, &SPIRV::IDRegClass);
}
SpirvTy = SpirvTy ? SpirvTy : GR->getOrCreateSPIRVType(Ty, MIB);
GR->assignSPIRVTypeToVReg(SpirvTy, Reg, MIB.getMF());
// This is to make it convenient for Legalizer to get the SPIRVType
// when processing the actual MI (i.e. not pseudo one).
Expand All @@ -290,11 +315,11 @@ Register insertAssignInstr(Register Reg, Type *Ty, SPIRVType *SpirvTy,

void processInstr(MachineInstr &MI, MachineIRBuilder &MIB,
MachineRegisterInfo &MRI, SPIRVGlobalRegistry *GR) {
unsigned Opc = MI.getOpcode();
assert(MI.getNumDefs() > 0 && MRI.hasOneUse(MI.getOperand(0).getReg()));
MachineInstr &AssignTypeInst =
*(MRI.use_instr_begin(MI.getOperand(0).getReg()));
auto NewReg = createNewIdReg(MI.getOperand(0).getReg(), Opc, MRI, *GR).first;
auto NewReg =
createNewIdReg(nullptr, MI.getOperand(0).getReg(), MRI, *GR).first;
AssignTypeInst.getOperand(1).setReg(NewReg);
MI.getOperand(0).setReg(NewReg);
MIB.setInsertPt(*MI.getParent(),
Expand All @@ -303,7 +328,7 @@ void processInstr(MachineInstr &MI, MachineIRBuilder &MIB,
for (auto &Op : MI.operands()) {
if (!Op.isReg() || Op.isDef())
continue;
auto IdOpInfo = createNewIdReg(Op.getReg(), Opc, MRI, *GR);
auto IdOpInfo = createNewIdReg(nullptr, Op.getReg(), MRI, *GR);
MIB.buildInstr(IdOpInfo.second).addDef(IdOpInfo.first).addUse(Op.getReg());
Op.setReg(IdOpInfo.first);
}
Expand Down Expand Up @@ -419,6 +444,7 @@ static void processInstrsWithTypeFolding(MachineFunction &MF,
processInstr(MI, MIB, MRI, GR);
}
}

for (MachineBasicBlock &MBB : MF) {
for (MachineInstr &MI : MBB) {
// We need to rewrite dst types for ASSIGN_TYPE instrs to be able
Expand All @@ -431,16 +457,18 @@ static void processInstrsWithTypeFolding(MachineFunction &MF,
if (!isTypeFoldingSupported(Opcode))
continue;
Register DstReg = MI.getOperand(0).getReg();
if (MRI.getType(DstReg).isVector())
bool IsDstPtr = MRI.getType(DstReg).isPointer();
if (IsDstPtr || MRI.getType(DstReg).isVector())
MRI.setRegClass(DstReg, &SPIRV::IDRegClass);
// Don't need to reset type of register holding constant and used in
// G_ADDRSPACE_CAST, since it braaks legalizer.
// G_ADDRSPACE_CAST, since it breaks legalizer.
if (Opcode == TargetOpcode::G_CONSTANT && MRI.hasOneUse(DstReg)) {
MachineInstr &UseMI = *MRI.use_instr_begin(DstReg);
if (UseMI.getOpcode() == TargetOpcode::G_ADDRSPACE_CAST)
continue;
}
MRI.setType(DstReg, LLT::scalar(32));
MRI.setType(DstReg, IsDstPtr ? LLT::pointer(0, GR->getPointerSize())
: LLT::scalar(32));
}
}
}
Expand Down
20 changes: 2 additions & 18 deletions llvm/lib/Target/SPIRV/SPIRVRegisterBankInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,7 @@ using namespace llvm;
const RegisterBank &
SPIRVRegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC,
LLT Ty) const {
switch (RC.getID()) {
case SPIRV::TYPERegClassID:
if (RC.getID() == SPIRV::TYPERegClassID)
return SPIRV::TYPERegBank;
case SPIRV::pIDRegClassID:
case SPIRV::IDRegClassID:
return SPIRV::IDRegBank;
case SPIRV::fIDRegClassID:
return SPIRV::fIDRegBank;
case SPIRV::vIDRegClassID:
return SPIRV::vIDRegBank;
case SPIRV::vfIDRegClassID:
return SPIRV::vfIDRegBank;
case SPIRV::vpIDRegClassID:
return SPIRV::vpIDRegBank;
case SPIRV::ANYIDRegClassID:
case SPIRV::ANYRegClassID:
return SPIRV::IDRegBank;
}
llvm_unreachable("Unknown register class");
return SPIRV::IDRegBank;
}
7 changes: 2 additions & 5 deletions llvm/lib/Target/SPIRV/SPIRVRegisterBanks.td
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@

// Although RegisterBankSelection is disabled we need to distinct the banks
// as InstructionSelector RegClass checking code relies on them
def IDRegBank : RegisterBank<"IDBank", [ID]>;
def fIDRegBank : RegisterBank<"fIDBank", [fID]>;
def vIDRegBank : RegisterBank<"vIDBank", [vID]>;
def vfIDRegBank : RegisterBank<"vfIDBank", [vfID]>;
def vpIDRegBank : RegisterBank<"vpIDBank", [vpID]>;

def TYPERegBank : RegisterBank<"TYPEBank", [TYPE]>;
def IDRegBank : RegisterBank<"IDBank", [ID, fID, pID32, pID64, vID, vfID, vpID32, vpID64]>;
49 changes: 28 additions & 21 deletions llvm/lib/Target/SPIRV/SPIRVRegisterInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,46 @@
//===----------------------------------------------------------------------===//

let Namespace = "SPIRV" in {
def p0 : PtrValueType <i32, 0>;

class P0Vec<ValueType scalar>
: PtrValueType <scalar, 0> {
let nElem = 2;
let ElementType = p0;
let isInteger = false;
let isFP = false;
let isVector = true;
// Pointer types for patterns with the GlobalISelEmitter
def p32 : PtrValueType <i32, 0>;
def p64 : PtrValueType <i64, 0>;

class VTPtrVec<int nelem, PtrValueType ptr>
: VTVec<nelem, ValueType<ptr.Size, ptr.Value>, ptr.Value> {
int isPointer = true;
}

def v2p0 : P0Vec<i32>;
// All registers are for 32-bit identifiers, so have a single dummy register
def v2p32 : VTPtrVec<2, p32>;
def v2p64 : VTPtrVec<2, p64>;

// Class for registers that are the result of OpTypeXXX instructions
// Class for type registers
def TYPE0 : Register<"TYPE0">;
def TYPE : RegisterClass<"SPIRV", [i32], 32, (add TYPE0)>;

// Class for every other non-type ID
// Class for non-type registers
def ID0 : Register<"ID0">;
def ID : RegisterClass<"SPIRV", [i32], 32, (add ID0)>;
def fID0 : Register<"fID0">;
def fID : RegisterClass<"SPIRV", [f32], 32, (add fID0)>;
def pID0 : Register<"pID0">;
def pID : RegisterClass<"SPIRV", [p0], 32, (add pID0)>;
def pID320 : Register<"pID320">;
def pID640 : Register<"pID640">;
def vID0 : Register<"vID0">;
def vID : RegisterClass<"SPIRV", [v2i32], 32, (add vID0)>;
def vfID0 : Register<"vfID0">;
def vpID320 : Register<"vpID320">;
def vpID640 : Register<"vpID640">;

def ID : RegisterClass<"SPIRV", [i32], 32, (add ID0)>;
def fID : RegisterClass<"SPIRV", [f32], 32, (add fID0)>;
def pID32 : RegisterClass<"SPIRV", [p32], 32, (add pID320)>;
def pID64 : RegisterClass<"SPIRV", [p64], 32, (add pID640)>;
def vID : RegisterClass<"SPIRV", [v2i32], 32, (add vID0)>;
def vfID : RegisterClass<"SPIRV", [v2f32], 32, (add vfID0)>;
def vpID0 : Register<"vpID0">;
def vpID : RegisterClass<"SPIRV", [v2p0], 32, (add vpID0)>;
def vpID32 : RegisterClass<"SPIRV", [v2p32], 32, (add vpID320)>;
def vpID64 : RegisterClass<"SPIRV", [v2p64], 32, (add vpID640)>;

def ANYID : RegisterClass<"SPIRV", [i32, f32, p0, v2i32, v2f32], 32, (add ID, fID, pID, vID, vfID)>;
def ANYID : RegisterClass<
"SPIRV",
[i32, f32, p32, p64, v2i32, v2f32, v2p32, v2p64],
32,
(add ID0, fID0, pID320, pID640, vID0, vfID0, vpID320, vpID640)>;

// A few instructions like OpName can take ids from both type and non-type
// instructions, so we need a super-class to allow for both to count as valid
Expand Down
4 changes: 4 additions & 0 deletions llvm/test/CodeGen/SPIRV/instructions/select-phi.ll
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
; RUN: llc -O0 -mtriple=spirv32-unknown-unknown %s -o - | FileCheck %s
; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s

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

; CHECK-DAG: %[[Char:.*]] = OpTypeInt 8 0
; CHECK-DAG: %[[Long:.*]] = OpTypeInt 32 0
Expand Down
25 changes: 25 additions & 0 deletions llvm/test/CodeGen/SPIRV/instructions/select-ptr-load.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 --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-DAG: %[[Float:.*]] = OpTypeFloat 32
; CHECK-SPIRV-DAG: %[[FloatPtr:.*]] = OpTypePointer Function %[[Float]]
; CHECK-SPIRV: OpInBoundsPtrAccessChain %[[FloatPtr]]
; CHECK-SPIRV: OpInBoundsPtrAccessChain %[[FloatPtr]]
; CHECK-SPIRV: OpSelect %[[FloatPtr]]
; CHECK-SPIRV: OpLoad %[[Float]]

%struct = type { [3 x float] }

define spir_kernel void @bar(i1 %sw) {
entry:
%var1 = alloca %struct
%var2 = alloca %struct
%elem1 = getelementptr inbounds [3 x float], ptr %var1, i64 0, i64 0
%elem2 = getelementptr inbounds [3 x float], ptr %var2, i64 0, i64 1
%elem = select i1 %sw, ptr %elem1, ptr %elem2
%res = load float, ptr %elem
ret void
}
3 changes: 3 additions & 0 deletions llvm/test/CodeGen/SPIRV/instructions/select.ll
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
; 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 %}

; 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 %}

; CHECK-DAG: OpName [[SCALARi32:%.+]] "select_i32"
; CHECK-DAG: OpName [[SCALARPTR:%.+]] "select_ptr"
; CHECK-DAG: OpName [[VEC2i32:%.+]] "select_i32v2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
; RUN: llc -O0 -mtriple=spirv64-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 %}
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}

; CHECK-SPIRV: OpSelect

Expand Down

0 comments on commit 5fdd419

Please sign in to comment.