[llvm] 214e6b4 - [SPIR-V] Inline assembly support (#93164)
via llvm-commits
llvm-commits at lists.llvm.org
Fri May 24 06:15:07 PDT 2024
Author: Vyacheslav Levytskyy
Date: 2024-05-24T15:15:03+02:00
New Revision: 214e6b40f8487cec03ab5d211369d8836de0ef68
URL: https://github.com/llvm/llvm-project/commit/214e6b40f8487cec03ab5d211369d8836de0ef68
DIFF: https://github.com/llvm/llvm-project/commit/214e6b40f8487cec03ab5d211369d8836de0ef68.diff
LOG: [SPIR-V] Inline assembly support (#93164)
This PR introduces support for inline assembly calls for SPIR-V Backend
in general, and support for SPV_INTEL_inline_assembly [1] extension in
particular. The former part of the PR is agnostic towards
vendor-specific requirements and resolves the task of supporting
successful transformation of inline assembly as long as it's possible
without specific SPIR-V instruction codes.
As a part of the PR there appears an opportunity to bring coherent
inline assembly information up to latest passes of the transformation
process (emitting final SPIR-V instructions), so that PR makes it easy
to add any another required flavor of inline assembly, other then
supported by the vendor specific SPV_INTEL_inline_assembly extension,
if/when needed.
At the moment, however, SPV_INTEL_inline_assembly is the only
implemented way to bring LLVM IR inline assembly calls up to valid
SPIR-V instructions and also the default one. This means that inline
assembly calls will generate an error message of such extension is not
used to prevent LLVM-generated error messages at the final stages of
translation. When the SPV_INTEL_inline_assembly extension is mentioned
among supported, translation of inline assembly is intercepted by this
extension implementation on a pre-legalizer step, and this is a place
where support for a new inline assembly extension may be added if
needed.
This PR also extends support for register classes, improves type
inference during pre-legalizer pass, and fixes a minor bug with
asm-printing of string literals.
[1]
https://github.com/intel/llvm/blob/sycl/sycl/doc/design/spirv-extensions/SPV_INTEL_inline_assembly.asciidoc
Added:
llvm/lib/Target/SPIRV/SPIRVInlineAsmLowering.cpp
llvm/lib/Target/SPIRV/SPIRVInlineAsmLowering.h
llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_inline_assembly/inline_asm.ll
Modified:
llvm/docs/SPIRVUsage.rst
llvm/include/llvm/IR/IntrinsicsSPIRV.td
llvm/lib/Target/SPIRV/CMakeLists.txt
llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
llvm/lib/Target/SPIRV/SPIRVISelLowering.h
llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp
llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
llvm/lib/Target/SPIRV/SPIRVRegisterBanks.td
llvm/lib/Target/SPIRV/SPIRVRegisterInfo.td
llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
llvm/lib/Target/SPIRV/SPIRVSubtarget.h
llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
Removed:
################################################################################
diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst
index d27177a4541a4..1f6157e53664a 100644
--- a/llvm/docs/SPIRVUsage.rst
+++ b/llvm/docs/SPIRVUsage.rst
@@ -143,6 +143,8 @@ list of supported SPIR-V extensions, sorted alphabetically by their extension na
- Adds instructions to convert between single-precision 32-bit floating-point values and 16-bit bfloat16 values.
* - ``SPV_INTEL_function_pointers``
- Allows translation of function pointers.
+ * - ``SPV_INTEL_inline_assembly``
+ - Allows to use inline assembly.
* - ``SPV_INTEL_optnone``
- Adds OptNoneINTEL value for Function Control mask that indicates a request to not optimize the function.
* - ``SPV_INTEL_subgroups``
@@ -333,6 +335,10 @@ SPIR-V backend, along with their descriptions and argument details.
- 32-bit Integer
- `[]`
- Generates an undefined value. Useful for optimizations and indicating uninitialized variables.
+ * - `int_spv_inline_asm`
+ - None
+ - `[Metadata, Metadata, Vararg]`
+ - Associates inline assembly features to inline assembly call instances by creating metadatas and preserving original arguments. Not emitted directly but used to support SPIR-V representation in LLVM IR.
* - `int_spv_assume`
- None
- `[1-bit Integer]`
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index cc84decc43407..90f12674d0470 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -36,6 +36,7 @@ let TargetPrefix = "spv" in {
def int_spv_alloca : Intrinsic<[llvm_any_ty], []>;
def int_spv_alloca_array : Intrinsic<[llvm_any_ty], [llvm_anyint_ty]>;
def int_spv_undef : Intrinsic<[llvm_i32_ty], []>;
+ def int_spv_inline_asm : Intrinsic<[], [llvm_metadata_ty, llvm_metadata_ty, llvm_vararg_ty]>;
// Expect, Assume Intrinsics
def int_spv_assume : Intrinsic<[], [llvm_i1_ty]>;
diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index 7001ac382f41c..fe09d5903045c 100644
--- a/llvm/lib/Target/SPIRV/CMakeLists.txt
+++ b/llvm/lib/Target/SPIRV/CMakeLists.txt
@@ -17,6 +17,7 @@ add_llvm_target(SPIRVCodeGen
SPIRVAsmPrinter.cpp
SPIRVBuiltins.cpp
SPIRVCallLowering.cpp
+ SPIRVInlineAsmLowering.cpp
SPIRVCommandLine.cpp
SPIRVDuplicatesTracker.cpp
SPIRVEmitIntrinsics.cpp
diff --git a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
index b468b71cc0efb..5c286acdcc9b3 100644
--- a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
@@ -321,14 +321,19 @@ void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
if (MI->getOperand(StrStartIndex).isReg())
break;
- std::string Str = getSPIRVStringOperand(*MI, OpNo);
+ std::string Str = getSPIRVStringOperand(*MI, StrStartIndex);
if (StrStartIndex != OpNo)
O << ' '; // Add a space if we're starting a new string/argument.
O << '"';
for (char c : Str) {
- if (c == '"')
- O.write('\\'); // Escape " characters (might break for complex UTF-8).
- O.write(c);
+ // Escape ", \n characters (might break for complex UTF-8).
+ if (c == '\n') {
+ O.write("\\n", 2);
+ } else {
+ if (c == '"')
+ O.write('\\');
+ O.write(c);
+ }
}
O << '"';
diff --git a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
index 752d71eddd99a..7f531542544ab 100644
--- a/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
@@ -47,6 +47,8 @@ static const std::map<std::string, SPIRV::Extension::Extension>
SPIRV::Extension::Extension::SPV_KHR_bit_instructions},
{"SPV_KHR_linkonce_odr",
SPIRV::Extension::Extension::SPV_KHR_linkonce_odr},
+ {"SPV_INTEL_inline_assembly",
+ SPIRV::Extension::Extension::SPV_INTEL_inline_assembly},
{"SPV_INTEL_bfloat16_conversion",
SPIRV::Extension::Extension::SPV_INTEL_bfloat16_conversion},
{"SPV_KHR_subgroup_rotate",
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index a1a08c5c699b6..ea53fe55e7ab5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -140,6 +140,7 @@ class SPIRVEmitIntrinsics
Instruction *visitAllocaInst(AllocaInst &I);
Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
Instruction *visitUnreachableInst(UnreachableInst &I);
+ Instruction *visitCallInst(CallInst &I);
StringRef getPassName() const override { return "SPIRV emit intrinsics"; }
@@ -629,6 +630,28 @@ void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) {
}
}
+Instruction *SPIRVEmitIntrinsics::visitCallInst(CallInst &Call) {
+ if (!Call.isInlineAsm())
+ return &Call;
+
+ const InlineAsm *IA = cast<InlineAsm>(Call.getCalledOperand());
+ LLVMContext &Ctx = F->getContext();
+
+ Constant *TyC = UndefValue::get(IA->getFunctionType());
+ MDString *ConstraintString = MDString::get(Ctx, IA->getConstraintString());
+ SmallVector<Value *> Args = {
+ MetadataAsValue::get(Ctx,
+ MDNode::get(Ctx, ValueAsMetadata::getConstant(TyC))),
+ MetadataAsValue::get(Ctx, MDNode::get(Ctx, ConstraintString))};
+ for (unsigned OpIdx = 0; OpIdx < Call.arg_size(); OpIdx++)
+ Args.push_back(Call.getArgOperand(OpIdx));
+
+ IRBuilder<> B(Call.getParent());
+ B.SetInsertPoint(&Call);
+ B.CreateIntrinsic(Intrinsic::spv_inline_asm, {}, {Args});
+ return &Call;
+}
+
Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &I) {
BasicBlock *ParentBB = I.getParent();
IRBuilder<> B(ParentBB);
diff --git a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
index 96b4a570a26b1..2bd22bbd63169 100644
--- a/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp
@@ -82,6 +82,28 @@ bool SPIRVTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info,
return false;
}
+std::pair<unsigned, const TargetRegisterClass *>
+SPIRVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
+ StringRef Constraint,
+ MVT VT) const {
+ const TargetRegisterClass *RC = nullptr;
+ if (Constraint.starts_with("{"))
+ return std::make_pair(0u, RC);
+
+ if (VT.isFloatingPoint())
+ RC = VT.isVector() ? &SPIRV::vfIDRegClass
+ : (VT.getScalarSizeInBits() > 32 ? &SPIRV::fID64RegClass
+ : &SPIRV::fIDRegClass);
+ else if (VT.isInteger())
+ RC = VT.isVector() ? &SPIRV::vIDRegClass
+ : (VT.getScalarSizeInBits() > 32 ? &SPIRV::ID64RegClass
+ : &SPIRV::IDRegClass);
+ else
+ RC = &SPIRV::IDRegClass;
+
+ return std::make_pair(0u, RC);
+}
+
// Insert a bitcast before the instruction to keep SPIR-V code valid
// when there is a type mismatch between results and operand types.
static void validatePtrTypes(const SPIRVSubtarget &STI,
diff --git a/llvm/lib/Target/SPIRV/SPIRVISelLowering.h b/llvm/lib/Target/SPIRV/SPIRVISelLowering.h
index 8c1de7d97d1a3..6fc200abf4627 100644
--- a/llvm/lib/Target/SPIRV/SPIRVISelLowering.h
+++ b/llvm/lib/Target/SPIRV/SPIRVISelLowering.h
@@ -55,6 +55,15 @@ class SPIRVTargetLowering : public TargetLowering {
MachineFunction &MF,
unsigned Intrinsic) const override;
+ std::pair<unsigned, const TargetRegisterClass *>
+ getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
+ StringRef Constraint, MVT VT) const override;
+ unsigned
+ getNumRegisters(LLVMContext &Context, EVT VT,
+ std::optional<MVT> RegisterVT = std::nullopt) const override {
+ return 1;
+ }
+
// Call the default implementation and finalize target lowering by inserting
// extra instructions required to preserve validity of SPIR-V code imposed by
// the standard.
diff --git a/llvm/lib/Target/SPIRV/SPIRVInlineAsmLowering.cpp b/llvm/lib/Target/SPIRV/SPIRVInlineAsmLowering.cpp
new file mode 100644
index 0000000000000..8bd4fb6bf8b11
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVInlineAsmLowering.cpp
@@ -0,0 +1,46 @@
+//===--- SPIRVInlineAsmLowering.cpp - Inline Asm lowering -------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the lowering of LLVM inline asm calls to machine code
+// calls for GlobalISel.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SPIRVInlineAsmLowering.h"
+#include "SPIRVSubtarget.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/IntrinsicsSPIRV.h"
+
+using namespace llvm;
+
+SPIRVInlineAsmLowering::SPIRVInlineAsmLowering(const SPIRVTargetLowering &TLI)
+ : InlineAsmLowering(&TLI) {}
+
+bool SPIRVInlineAsmLowering::lowerAsmOperandForConstraint(
+ Value *Val, StringRef Constraint, std::vector<MachineOperand> &Ops,
+ MachineIRBuilder &MIRBuilder) const {
+ Value *ValOp = nullptr;
+ if (isa<ConstantInt>(Val)) {
+ ValOp = Val;
+ } else if (ConstantFP *CFP = dyn_cast<ConstantFP>(Val)) {
+ Ops.push_back(MachineOperand::CreateFPImm(CFP));
+ return true;
+ } else if (auto *II = dyn_cast<IntrinsicInst>(Val)) {
+ if (II->getIntrinsicID() == Intrinsic::spv_track_constant) {
+ if (isa<ConstantInt>(II->getOperand(0))) {
+ ValOp = II->getOperand(0);
+ } else if (ConstantFP *CFP = dyn_cast<ConstantFP>(II->getOperand(0))) {
+ Ops.push_back(MachineOperand::CreateFPImm(CFP));
+ return true;
+ }
+ }
+ }
+ return ValOp ? InlineAsmLowering::lowerAsmOperandForConstraint(
+ ValOp, Constraint, Ops, MIRBuilder)
+ : false;
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRVInlineAsmLowering.h b/llvm/lib/Target/SPIRV/SPIRVInlineAsmLowering.h
new file mode 100644
index 0000000000000..72291855a18c4
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVInlineAsmLowering.h
@@ -0,0 +1,33 @@
+//===--- SPIRVInlineAsmLowering.h - Inline Asm lowering ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes how to lower LLVM inline asm calls to machine
+// code calls for GlobalISel.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVINLINEASMLOWERING_H
+#define LLVM_LIB_TARGET_SPIRV_SPIRVINLINEASMLOWERING_H
+
+#include "llvm/CodeGen/GlobalISel/InlineAsmLowering.h"
+
+namespace llvm {
+
+class SPIRVTargetLowering;
+
+class SPIRVInlineAsmLowering : public InlineAsmLowering {
+public:
+ SPIRVInlineAsmLowering(const SPIRVTargetLowering &TLI);
+ bool
+ lowerAsmOperandForConstraint(Value *Val, StringRef Constraint,
+ std::vector<MachineOperand> &Ops,
+ MachineIRBuilder &MIRBuilder) const override;
+};
+} // end namespace llvm
+
+#endif // LLVM_LIB_TARGET_SPIRV_SPIRVINLINEASMLOWERING_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
index af98f2f880459..12cf7613a45cf 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.cpp
@@ -47,6 +47,16 @@ bool SPIRVInstrInfo::isConstantInstr(const MachineInstr &MI) const {
}
}
+bool SPIRVInstrInfo::isInlineAsmDefInstr(const MachineInstr &MI) const {
+ switch (MI.getOpcode()) {
+ case SPIRV::OpAsmTargetINTEL:
+ case SPIRV::OpAsmINTEL:
+ return true;
+ default:
+ return false;
+ }
+}
+
bool SPIRVInstrInfo::isTypeDeclInstr(const MachineInstr &MI) const {
auto &MRI = MI.getMF()->getRegInfo();
if (MI.getNumDefs() >= 1 && MI.getOperand(0).isReg()) {
@@ -246,7 +256,8 @@ void SPIRVInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
}
bool SPIRVInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
- if (MI.getOpcode() == SPIRV::GET_ID || MI.getOpcode() == SPIRV::GET_fID ||
+ if (MI.getOpcode() == SPIRV::GET_ID || MI.getOpcode() == SPIRV::GET_ID64 ||
+ MI.getOpcode() == SPIRV::GET_fID || MI.getOpcode() == SPIRV::GET_fID64 ||
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 ||
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
index 4f2781c9404b8..95f3874913572 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.h
@@ -30,6 +30,7 @@ class SPIRVInstrInfo : public SPIRVGenInstrInfo {
const SPIRVRegisterInfo &getRegisterInfo() const { return RI; }
bool isHeaderInstr(const MachineInstr &MI) const;
bool isConstantInstr(const MachineInstr &MI) const;
+ bool isInlineAsmDefInstr(const MachineInstr &MI) const;
bool isTypeDeclInstr(const MachineInstr &MI) const;
bool isDecorationInstr(const MachineInstr &MI) const;
bool canUseFastMathFlags(const MachineInstr &MI) const;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
index a6bedab6d4ee5..7c9b84a48a2a7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
+++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td
@@ -18,7 +18,9 @@ let isCodeGenOnly=1 in {
def ASSIGN_TYPE: Pseudo<(outs ANYID:$dst_id), (ins ANYID:$src_id, TYPE:$src_ty)>;
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_ID64: Pseudo<(outs ID64:$dst_id), (ins ANYID:$src)>;
def GET_fID: Pseudo<(outs fID:$dst_id), (ins ANYID:$src)>;
+ def GET_fID64: Pseudo<(outs fID64:$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)>;
@@ -854,3 +856,11 @@ def OpGroupLogicalOrKHR: Op<6407, (outs ID:$res), (ins TYPE:$type, ID:$scope, i3
"$res = OpGroupLogicalOrKHR $type $scope $groupOp $value">;
def OpGroupLogicalXorKHR: Op<6408, (outs ID:$res), (ins TYPE:$type, ID:$scope, i32imm:$groupOp, ID:$value),
"$res = OpGroupLogicalXorKHR $type $scope $groupOp $value">;
+
+// Inline Assembly Instructions
+def OpAsmTargetINTEL: Op<5609, (outs ID:$res), (ins StringImm:$str), "$res = OpAsmTargetINTEL $str">;
+def OpAsmINTEL: Op<5610, (outs ID:$res), (ins TYPE:$type, TYPE:$asm_type, ID:$target,
+ StringImm:$asm, StringImm:$constraints),
+ "$res = OpAsmINTEL $type $asm_type $target $asm">;
+def OpAsmCallINTEL: Op<5611, (outs ID:$res), (ins TYPE:$type, ID:$asm, variable_ops),
+ "$res = OpAsmCallINTEL $type $asm">;
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
index cbe7c5ca30570..c86ab285f354f 100644
--- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -1151,6 +1151,14 @@ void addInstrRequirements(const MachineInstr &MI,
Reqs.addCapability(SPIRV::Capability::VariableLengthArrayINTEL);
}
break;
+ case SPIRV::OpAsmTargetINTEL:
+ case SPIRV::OpAsmINTEL:
+ case SPIRV::OpAsmCallINTEL:
+ if (ST.canUseExtension(SPIRV::Extension::SPV_INTEL_inline_assembly)) {
+ Reqs.addExtension(SPIRV::Extension::SPV_INTEL_inline_assembly);
+ Reqs.addCapability(SPIRV::Capability::AsmINTEL);
+ }
+ break;
default:
break;
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp
index d652b5de60808..c3842f0266706 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPostLegalizer.cpp
@@ -54,7 +54,8 @@ extern void processInstr(MachineInstr &MI, MachineIRBuilder &MIB,
} // namespace llvm
static bool isMetaInstrGET(unsigned Opcode) {
- return Opcode == SPIRV::GET_ID || Opcode == SPIRV::GET_fID ||
+ return Opcode == SPIRV::GET_ID || Opcode == SPIRV::GET_ID64 ||
+ Opcode == SPIRV::GET_fID || Opcode == SPIRV::GET_fID64 ||
Opcode == SPIRV::GET_pID32 || Opcode == SPIRV::GET_pID64 ||
Opcode == SPIRV::GET_vID || Opcode == SPIRV::GET_vfID ||
Opcode == SPIRV::GET_vpID32 || Opcode == SPIRV::GET_vpID64;
diff --git a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
index 9bff23dd96668..85299a49a6b94 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPreLegalizer.cpp
@@ -215,6 +215,8 @@ static SPIRVType *propagateSPIRVType(MachineInstr *MI, SPIRVGlobalRegistry *GR,
SpirvTy = GR->getOrCreateSPIRVType(Ty, MIB);
break;
}
+ case TargetOpcode::G_ANYEXT:
+ case TargetOpcode::G_SEXT:
case TargetOpcode::G_ZEXT: {
if (MI->getOperand(1).isReg()) {
if (MachineInstr *DefInstr =
@@ -457,12 +459,7 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
Ty = VectorType::get(ElemTy, NumElts, false);
}
insertAssignInstr(Reg, Ty, nullptr, GR, MIB, MRI);
- } else if (MI.getOpcode() == TargetOpcode::G_TRUNC ||
- MI.getOpcode() == TargetOpcode::G_ZEXT ||
- MI.getOpcode() == TargetOpcode::G_PTRTOINT ||
- MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE ||
- MI.getOpcode() == TargetOpcode::COPY ||
- MI.getOpcode() == TargetOpcode::G_ADDRSPACE_CAST) {
+ } else if (MI.getOpcode() == TargetOpcode::G_GLOBAL_VALUE) {
propagateSPIRVType(&MI, GR, MRI, MIB);
}
@@ -474,6 +471,24 @@ generateAssignInstrs(MachineFunction &MF, SPIRVGlobalRegistry *GR,
}
for (MachineInstr *MI : ToErase)
MI->eraseFromParent();
+
+ // Address the case when IRTranslator introduces instructions with new
+ // registers without SPIRVType associated.
+ for (MachineBasicBlock &MBB : MF) {
+ for (MachineInstr &MI : MBB) {
+ switch (MI.getOpcode()) {
+ case TargetOpcode::G_TRUNC:
+ case TargetOpcode::G_ANYEXT:
+ case TargetOpcode::G_SEXT:
+ case TargetOpcode::G_ZEXT:
+ case TargetOpcode::G_PTRTOINT:
+ case TargetOpcode::COPY:
+ case TargetOpcode::G_ADDRSPACE_CAST:
+ propagateSPIRVType(&MI, GR, MRI, MIB);
+ break;
+ }
+ }
+ }
}
// Defined in SPIRVLegalizerInfo.cpp.
@@ -519,6 +534,128 @@ static void processInstrsWithTypeFolding(MachineFunction &MF,
}
}
+static void
+insertInlineAsmProcess(MachineFunction &MF, SPIRVGlobalRegistry *GR,
+ const SPIRVSubtarget &ST, MachineIRBuilder MIRBuilder,
+ const SmallVector<MachineInstr *> &ToProcess) {
+ MachineRegisterInfo &MRI = MF.getRegInfo();
+ Register AsmTargetReg;
+ for (unsigned i = 0, Sz = ToProcess.size(); i + 1 < Sz; i += 2) {
+ MachineInstr *I1 = ToProcess[i], *I2 = ToProcess[i + 1];
+ assert(isSpvIntrinsic(*I1, Intrinsic::spv_inline_asm) && I2->isInlineAsm());
+ MIRBuilder.setInsertPt(*I1->getParent(), *I1);
+
+ if (!AsmTargetReg.isValid()) {
+ // define vendor specific assembly target or dialect
+ AsmTargetReg = MRI.createGenericVirtualRegister(LLT::scalar(32));
+ MRI.setRegClass(AsmTargetReg, &SPIRV::IDRegClass);
+ auto AsmTargetMIB =
+ MIRBuilder.buildInstr(SPIRV::OpAsmTargetINTEL).addDef(AsmTargetReg);
+ addStringImm(ST.getTargetTripleAsStr(), AsmTargetMIB);
+ GR->add(AsmTargetMIB.getInstr(), &MF, AsmTargetReg);
+ }
+
+ // create types
+ const MDNode *IAMD = I1->getOperand(1).getMetadata();
+ FunctionType *FTy = cast<FunctionType>(getMDOperandAsType(IAMD, 0));
+ SmallVector<SPIRVType *, 4> ArgTypes;
+ for (const auto &ArgTy : FTy->params())
+ ArgTypes.push_back(GR->getOrCreateSPIRVType(ArgTy, MIRBuilder));
+ SPIRVType *RetType =
+ GR->getOrCreateSPIRVType(FTy->getReturnType(), MIRBuilder);
+ SPIRVType *FuncType = GR->getOrCreateOpTypeFunctionWithArgs(
+ FTy, RetType, ArgTypes, MIRBuilder);
+
+ // define vendor specific assembly instructions string
+ Register AsmReg = MRI.createGenericVirtualRegister(LLT::scalar(32));
+ MRI.setRegClass(AsmReg, &SPIRV::IDRegClass);
+ auto AsmMIB = MIRBuilder.buildInstr(SPIRV::OpAsmINTEL)
+ .addDef(AsmReg)
+ .addUse(GR->getSPIRVTypeID(RetType))
+ .addUse(GR->getSPIRVTypeID(FuncType))
+ .addUse(AsmTargetReg);
+ // inline asm string:
+ addStringImm(I2->getOperand(InlineAsm::MIOp_AsmString).getSymbolName(),
+ AsmMIB);
+ // inline asm constraint string:
+ addStringImm(cast<MDString>(I1->getOperand(2).getMetadata()->getOperand(0))
+ ->getString(),
+ AsmMIB);
+ GR->add(AsmMIB.getInstr(), &MF, AsmReg);
+
+ // calls the inline assembly instruction
+ unsigned ExtraInfo = I2->getOperand(InlineAsm::MIOp_ExtraInfo).getImm();
+ if (ExtraInfo & InlineAsm::Extra_HasSideEffects)
+ MIRBuilder.buildInstr(SPIRV::OpDecorate)
+ .addUse(AsmReg)
+ .addImm(static_cast<uint32_t>(SPIRV::Decoration::SideEffectsINTEL));
+ Register DefReg;
+ SmallVector<unsigned, 4> Ops;
+ unsigned StartOp = InlineAsm::MIOp_FirstOperand,
+ AsmDescOp = InlineAsm::MIOp_FirstOperand;
+ unsigned I2Sz = I2->getNumOperands();
+ for (unsigned Idx = StartOp; Idx != I2Sz; ++Idx) {
+ const MachineOperand &MO = I2->getOperand(Idx);
+ if (MO.isMetadata())
+ continue;
+ if (Idx == AsmDescOp && MO.isImm()) {
+ // compute the index of the next operand descriptor
+ const InlineAsm::Flag F(MO.getImm());
+ AsmDescOp += 1 + F.getNumOperandRegisters();
+ } else {
+ if (MO.isReg() && MO.isDef())
+ DefReg = MO.getReg();
+ else
+ Ops.push_back(Idx);
+ }
+ }
+ if (!DefReg.isValid()) {
+ DefReg = MRI.createGenericVirtualRegister(LLT::scalar(32));
+ MRI.setRegClass(DefReg, &SPIRV::IDRegClass);
+ SPIRVType *VoidType = GR->getOrCreateSPIRVType(
+ Type::getVoidTy(MF.getFunction().getContext()), MIRBuilder);
+ GR->assignSPIRVTypeToVReg(VoidType, DefReg, MF);
+ }
+ auto AsmCall = MIRBuilder.buildInstr(SPIRV::OpAsmCallINTEL)
+ .addDef(DefReg)
+ .addUse(GR->getSPIRVTypeID(RetType))
+ .addUse(AsmReg);
+ unsigned IntrIdx = 2;
+ for (unsigned Idx : Ops) {
+ ++IntrIdx;
+ const MachineOperand &MO = I2->getOperand(Idx);
+ if (MO.isReg())
+ AsmCall.addUse(MO.getReg());
+ else
+ AsmCall.addUse(I1->getOperand(IntrIdx).getReg());
+ }
+ }
+ for (MachineInstr *MI : ToProcess)
+ MI->eraseFromParent();
+}
+
+static void insertInlineAsm(MachineFunction &MF, SPIRVGlobalRegistry *GR,
+ const SPIRVSubtarget &ST,
+ MachineIRBuilder MIRBuilder) {
+ SmallVector<MachineInstr *> ToProcess;
+ for (MachineBasicBlock &MBB : MF) {
+ for (MachineInstr &MI : MBB) {
+ if (isSpvIntrinsic(MI, Intrinsic::spv_inline_asm) ||
+ MI.getOpcode() == TargetOpcode::INLINEASM)
+ ToProcess.push_back(&MI);
+ }
+ }
+ if (ToProcess.size() == 0)
+ return;
+
+ if (!ST.canUseExtension(SPIRV::Extension::SPV_INTEL_inline_assembly))
+ report_fatal_error("Inline assembly instructions require the "
+ "following SPIR-V extension: SPV_INTEL_inline_assembly",
+ false);
+
+ insertInlineAsmProcess(MF, GR, ST, MIRBuilder, ToProcess);
+}
+
static void insertSpirvDecorations(MachineFunction &MF, MachineIRBuilder MIB) {
SmallVector<MachineInstr *, 10> ToErase;
for (MachineBasicBlock &MBB : MF) {
@@ -673,6 +810,7 @@ bool SPIRVPreLegalizer::runOnMachineFunction(MachineFunction &MF) {
processInstrsWithTypeFolding(MF, GR, MIB);
removeImplicitFallthroughs(MF, MIB);
insertSpirvDecorations(MF, MIB);
+ insertInlineAsm(MF, GR, ST, MIB);
return true;
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVRegisterBanks.td b/llvm/lib/Target/SPIRV/SPIRVRegisterBanks.td
index dea2ef402d3d9..e81d969564046 100644
--- a/llvm/lib/Target/SPIRV/SPIRVRegisterBanks.td
+++ b/llvm/lib/Target/SPIRV/SPIRVRegisterBanks.td
@@ -10,4 +10,4 @@
// as InstructionSelector RegClass checking code relies on them
def TYPERegBank : RegisterBank<"TYPEBank", [TYPE]>;
-def IDRegBank : RegisterBank<"IDBank", [ID, fID, pID32, pID64, vID, vfID, vpID32, vpID64]>;
+def IDRegBank : RegisterBank<"IDBank", [ID, ID64, fID, fID64, pID32, pID64, vID, vfID, vpID32, vpID64]>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVRegisterInfo.td b/llvm/lib/Target/SPIRV/SPIRVRegisterInfo.td
index 9231d22e8d836..17f6ba59cc5de 100644
--- a/llvm/lib/Target/SPIRV/SPIRVRegisterInfo.td
+++ b/llvm/lib/Target/SPIRV/SPIRVRegisterInfo.td
@@ -29,7 +29,9 @@ let Namespace = "SPIRV" in {
// Class for non-type registers
def ID0 : Register<"ID0">;
+ def ID640 : Register<"ID640">;
def fID0 : Register<"fID0">;
+ def fID640 : Register<"fID640">;
def pID320 : Register<"pID320">;
def pID640 : Register<"pID640">;
def vID0 : Register<"vID0">;
@@ -38,7 +40,9 @@ let Namespace = "SPIRV" in {
def vpID640 : Register<"vpID640">;
def ID : RegisterClass<"SPIRV", [i32], 32, (add ID0)>;
+ def ID64 : RegisterClass<"SPIRV", [i64], 32, (add ID640)>;
def fID : RegisterClass<"SPIRV", [f32], 32, (add fID0)>;
+ def fID64 : RegisterClass<"SPIRV", [f64], 32, (add fID640)>;
def pID32 : RegisterClass<"SPIRV", [p32], 32, (add pID320)>;
def pID64 : RegisterClass<"SPIRV", [p64], 32, (add pID640)>;
def vID : RegisterClass<"SPIRV", [v2i32], 32, (add vID0)>;
@@ -48,9 +52,9 @@ let Namespace = "SPIRV" in {
def ANYID : RegisterClass<
"SPIRV",
- [i32, f32, p32, p64, v2i32, v2f32, v2p32, v2p64],
+ [i32, i64, f32, f64, p32, p64, v2i32, v2f32, v2p32, v2p64],
32,
- (add ID0, fID0, pID320, pID640, vID0, vfID0, vpID320, vpID640)>;
+ (add ID0, ID640, fID0, fID640, 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
diff --git a/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp b/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
index 7aa0c566c75f3..27472923ee08c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
@@ -82,6 +82,7 @@ SPIRVSubtarget::SPIRVSubtarget(const Triple &TT, const std::string &CPU,
GR = std::make_unique<SPIRVGlobalRegistry>(PointerSize);
CallLoweringInfo = std::make_unique<SPIRVCallLowering>(TLInfo, GR.get());
+ InlineAsmInfo = std::make_unique<SPIRVInlineAsmLowering>(TLInfo);
Legalizer = std::make_unique<SPIRVLegalizerInfo>(*this);
RegBankInfo = std::make_unique<SPIRVRegisterBankInfo>();
InstSelector.reset(
diff --git a/llvm/lib/Target/SPIRV/SPIRVSubtarget.h b/llvm/lib/Target/SPIRV/SPIRVSubtarget.h
index 3e4044084266c..211216488db79 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSubtarget.h
+++ b/llvm/lib/Target/SPIRV/SPIRVSubtarget.h
@@ -16,6 +16,7 @@
#include "SPIRVCallLowering.h"
#include "SPIRVFrameLowering.h"
#include "SPIRVISelLowering.h"
+#include "SPIRVInlineAsmLowering.h"
#include "SPIRVInstrInfo.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/CodeGen/GlobalISel/CallLowering.h"
@@ -54,6 +55,7 @@ class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
std::unique_ptr<RegisterBankInfo> RegBankInfo;
std::unique_ptr<LegalizerInfo> Legalizer;
std::unique_ptr<InstructionSelector> InstSelector;
+ std::unique_ptr<InlineAsmLowering> InlineAsmInfo;
// TODO: Initialise the available extensions, extended instruction sets
// based on the environment settings.
@@ -81,6 +83,7 @@ class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
TargetTriple.getArch() == Triple::spirv64;
}
bool isVulkanEnv() const { return TargetTriple.getArch() == Triple::spirv; }
+ const std::string &getTargetTripleAsStr() const { return TargetTriple.str(); }
VersionTuple getSPIRVVersion() const { return SPIRVVersion; };
bool isAtLeastSPIRVVer(VersionTuple VerToCompareTo) const;
bool isAtLeastOpenCLVer(VersionTuple VerToCompareTo) const;
@@ -108,6 +111,9 @@ class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
InstructionSelector *getInstructionSelector() const override {
return InstSelector.get();
}
+ const InlineAsmLowering *getInlineAsmLowering() const override {
+ return InlineAsmInfo.get();
+ }
const SPIRVInstrInfo *getInstrInfo() const override { return &InstrInfo; }
const SPIRVFrameLowering *getFrameLowering() const override {
return &FrameLowering;
diff --git a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
index 50d327179fa84..98cbd9d2c1f2e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
+++ b/llvm/lib/Target/SPIRV/SPIRVSymbolicOperands.td
@@ -298,6 +298,7 @@ defm SPV_INTEL_optnone : ExtensionOperand<103>;
defm SPV_INTEL_function_pointers : ExtensionOperand<104>;
defm SPV_INTEL_variable_length_array : ExtensionOperand<105>;
defm SPV_INTEL_bfloat16_conversion : ExtensionOperand<106>;
+defm SPV_INTEL_inline_assembly : ExtensionOperand<107>;
//===----------------------------------------------------------------------===//
// Multiclass used to define Capabilities enum values and at the same time
@@ -458,6 +459,7 @@ defm BitInstructions : CapabilityOperand<6025, 0, 0, [SPV_KHR_bit_instructions],
defm ExpectAssumeKHR : CapabilityOperand<5629, 0, 0, [SPV_KHR_expect_assume], []>;
defm FunctionPointersINTEL : CapabilityOperand<5603, 0, 0, [SPV_INTEL_function_pointers], []>;
defm IndirectReferencesINTEL : CapabilityOperand<5604, 0, 0, [SPV_INTEL_function_pointers], []>;
+defm AsmINTEL : CapabilityOperand<5606, 0, 0, [SPV_INTEL_inline_assembly], []>;
defm GroupNonUniformRotateKHR : CapabilityOperand<6026, 0, 0, [SPV_KHR_subgroup_rotate], [GroupNonUniform]>;
defm AtomicFloat32AddEXT : CapabilityOperand<6033, 0, 0, [SPV_EXT_shader_atomic_float_add], []>;
defm AtomicFloat64AddEXT : CapabilityOperand<6034, 0, 0, [SPV_EXT_shader_atomic_float_add], []>;
@@ -1201,6 +1203,8 @@ defm UserSemantic : DecorationOperand<5635, 0, 0, [], []>;
defm RestrictPointerEXT : DecorationOperand<5355, 0, 0, [], [PhysicalStorageBufferAddressesEXT]>;
defm AliasedPointerEXT : DecorationOperand<5356, 0, 0, [], [PhysicalStorageBufferAddressesEXT]>;
defm ReferencedIndirectlyINTEL : DecorationOperand<5602, 0, 0, [], [IndirectReferencesINTEL]>;
+defm ClobberINTEL : DecorationOperand<5607, 0, 0, [SPV_INTEL_inline_assembly], [AsmINTEL]>;
+defm SideEffectsINTEL : DecorationOperand<5608, 0, 0, [SPV_INTEL_inline_assembly], [AsmINTEL]>;
defm ArgumentAttributeINTEL : DecorationOperand<6409, 0, 0, [], [FunctionPointersINTEL]>;
//===----------------------------------------------------------------------===//
diff --git a/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_inline_assembly/inline_asm.ll b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_inline_assembly/inline_asm.ll
new file mode 100644
index 0000000000000..449dd71954500
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/extensions/SPV_INTEL_inline_assembly/inline_asm.ll
@@ -0,0 +1,93 @@
+; RUN: llc -O0 -mtriple=spirv64-unknown-unknown %s --spirv-ext=+SPV_INTEL_inline_assembly -o - | FileCheck %s
+; TODO: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s --spirv-ext=+SPV_INTEL_inline_assembly -o - -filetype=obj | spirv-val %}
+
+; RUN: not llc -O0 -mtriple=spirv64-unknown-unknown %s -o %t.spvt 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR
+; CHECK-ERROR: Inline assembly instructions require the following SPIR-V extension: SPV_INTEL_inline_assembly
+
+; CHECK: OpCapability AsmINTEL
+; CHECK: OpExtension "SPV_INTEL_inline_assembly"
+
+; CHECK-COUNT-8: OpDecorate %[[#]] SideEffectsINTEL
+
+; CHECK-DAG: %[[#VoidTy:]] = OpTypeVoid
+; CHECK-DAG: %[[#Int8Ty:]] = OpTypeInt 8 0
+; CHECK-DAG: %[[#Int32Ty:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#Int64Ty:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#HalfTy:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#FloatTy:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#DoubleTy:]] = OpTypeFloat 64
+
+; CHECK-DAG: OpTypeFunction %[[#VoidTy]] %[[#]] %[[#]] %[[#]] %[[#Int64Ty]]
+; CHECK-DAG: %[[#Fun1Ty:]] = OpTypeFunction %[[#VoidTy]]
+; CHECK-DAG: %[[#Fun2Ty:]] = OpTypeFunction %[[#Int32Ty]]
+; CHECK-DAG: %[[#Fun3Ty:]] = OpTypeFunction %[[#Int32Ty]] %[[#Int32Ty]]
+; CHECK-DAG: %[[#Fun4Ty:]] = OpTypeFunction %[[#FloatTy]] %[[#FloatTy]]
+; CHECK-DAG: %[[#Fun5Ty:]] = OpTypeFunction %[[#HalfTy]] %[[#FloatTy]] %[[#FloatTy]]
+; CHECK-DAG: %[[#Fun6Ty:]] = OpTypeFunction %[[#Int8Ty]] %[[#FloatTy]] %[[#Int32Ty]] %[[#Int8Ty]]
+; CHECK-DAG: %[[#Fun7Ty:]] = OpTypeFunction %[[#Int64Ty]] %[[#Int64Ty]] %[[#Int32Ty]] %[[#Int8Ty]]
+; CHECK-DAG: %[[#Fun8Ty:]] = OpTypeFunction %[[#VoidTy]] %[[#Int32Ty]] %[[#DoubleTy]]
+
+; CHECK-DAG: %[[#Const2:]] = OpConstant %[[#FloatTy]] 2
+; CHECK-DAG: %[[#Const123:]] = OpConstant %[[#Int32Ty]] 123
+; CHECK-DAG: %[[#Const42:]] = OpConstant %[[#DoubleTy:]] 42
+
+; CHECK: %[[#Dialect:]] = OpAsmTargetINTEL "spirv64-unknown-unknown"
+; CHECK-NO: OpAsmTargetINTEL
+
+; CHECK: %[[#Asm1:]] = OpAsmINTEL %[[#VoidTy]] %[[#Fun1Ty]] %[[#Dialect]] "" ""
+; CHECK: %[[#Asm2:]] = OpAsmINTEL %[[#VoidTy]] %[[#Fun1Ty]] %[[#Dialect]] "nop" ""
+; CHECK: %[[#Asm3:]] = OpAsmINTEL %[[#VoidTy]] %[[#Fun1Ty]] %[[#Dialect]] "" "~{cc},~{memory}"
+; CHECK: %[[#Asm4:]] = OpAsmINTEL %[[#Int32Ty]] %[[#Fun2Ty:]] %[[#Dialect]] "clobber_out $0" "=&r"
+; CHECK: %[[#Asm5:]] = OpAsmINTEL %[[#Int32Ty]] %[[#Fun3Ty]] %[[#Dialect]] "icmd $0 $1" "=r,r"
+; CHECK: %[[#Asm6:]] = OpAsmINTEL %[[#FloatTy]] %[[#Fun4Ty]] %[[#Dialect]] "fcmd $0 $1" "=r,r"
+; CHECK: %[[#Asm7:]] = OpAsmINTEL %[[#HalfTy]] %[[#Fun5Ty]] %[[#Dialect]] "fcmdext $0 $1 $2" "=r,r,r"
+; CHECK: %[[#Asm8:]] = OpAsmINTEL %[[#Int8Ty]] %[[#Fun6Ty]] %[[#Dialect]] "cmdext $0 $3 $1 $2" "=r,r,r,r"
+; CHECK: %[[#Asm9:]] = OpAsmINTEL %[[#Int64Ty]] %[[#Fun7Ty]] %[[#Dialect]] "icmdext $0 $3 $1 $2" "=r,r,r,r"
+; CHECK: %[[#Asm10:]] = OpAsmINTEL %[[#VoidTy]] %[[#Fun8Ty]] %[[#Dialect]] "constcmd $0 $1" "r,r"
+; CHECK: %[[#Asm11:]] = OpAsmINTEL %[[#VoidTy]] %[[#Fun8Ty]] %[[#Dialect]] "constcmd $0 $1" "i,i"
+; CHECK-NO: OpAsmINTEL
+
+; CHECK: OpFunction
+; CHECK: OpAsmCallINTEL %[[#VoidTy]] %[[#Asm1]]
+; CHECK: OpAsmCallINTEL %[[#VoidTy]] %[[#Asm2]]
+; CHECK: OpAsmCallINTEL %[[#VoidTy]] %[[#Asm3]]
+; CHECK: OpAsmCallINTEL %[[#Int32Ty]] %[[#Asm4]]
+; CHECK: OpAsmCallINTEL %[[#Int32Ty]] %[[#Asm5]] %[[#]]
+; CHECK: OpAsmCallINTEL %[[#FloatTy]] %[[#Asm6]] %[[#]]
+; CHECK: OpAsmCallINTEL %[[#HalfTy]] %[[#Asm7]] %[[#Const2]] %[[#]]
+; CHECK: OpAsmCallINTEL %[[#Int8Ty]] %[[#Asm8]] %[[#]] %[[#Const123]] %[[#]]
+; CHECK: OpAsmCallINTEL %[[#Int64Ty]] %[[#Asm9]] %[[#]] %[[#]] %[[#]]
+; CHECK: OpAsmCallINTEL %[[#VoidTy]] %[[#Asm10]] %[[#Const123]] %[[#Const42]]
+; CHECK: OpAsmCallINTEL %[[#VoidTy]] %[[#Asm11]] %[[#Const123]] %[[#Const42]]
+; CHECK-NO: OpAsmCallINTEL
+
+define spir_kernel void @foo(ptr addrspace(1) %_arg_int, ptr addrspace(1) %_arg_float, ptr addrspace(1) %_arg_half, i64 %_lng) {
+ %i1 = load i32, ptr addrspace(1) %_arg_int
+ %i2 = load i8, ptr addrspace(1) %_arg_int
+ %f1 = load float, ptr addrspace(1) %_arg_float
+ %h1 = load half, ptr addrspace(1) %_arg_half
+ ; inline asm
+ call void asm sideeffect "", ""()
+ call void asm sideeffect "nop", ""()
+ call void asm sideeffect "", "~{cc},~{memory}"()
+ %res_i0 = call i32 asm "clobber_out $0", "=&r"()
+ store i32 %res_i0, ptr addrspace(1) %_arg_int
+ ; inline asm: integer
+ %res_i1 = call i32 asm sideeffect "icmd $0 $1", "=r,r"(i32 %i1)
+ store i32 %res_i1, ptr addrspace(1) %_arg_int
+ ; inline asm: float
+ %res_f1 = call float asm sideeffect "fcmd $0 $1", "=r,r"(float %f1)
+ store float %res_f1, ptr addrspace(1) %_arg_float
+ ; inline asm: mixed floats
+ %res_f2 = call half asm sideeffect "fcmdext $0 $1 $2", "=r,r,r"(float 2.0, float %f1)
+ store half %res_f2, ptr addrspace(1) %_arg_half
+ ; inline asm: mixed operands of
diff erent types
+ call i8 asm sideeffect "cmdext $0 $3 $1 $2", "=r,r,r,r"(float %f1, i32 123, i8 %i2)
+ ; inline asm: mixed integers
+ %res_i2 = call i64 asm sideeffect "icmdext $0 $3 $1 $2", "=r,r,r,r"(i64 %_lng, i32 %i1, i8 %i2)
+ store i64 %res_i2, ptr addrspace(1) %_arg_int
+ ; inline asm: constant arguments, misc constraints
+ call void asm "constcmd $0 $1", "r,r"(i32 123, double 42.0)
+ call void asm "constcmd $0 $1", "i,i"(i32 123, double 42.0)
+ ret void
+}
More information about the llvm-commits
mailing list