Skip to content

Commit 214e6b4

Browse files
[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
1 parent 0ad4c80 commit 214e6b4

22 files changed

+440
-15
lines changed

llvm/docs/SPIRVUsage.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ list of supported SPIR-V extensions, sorted alphabetically by their extension na
143143
- Adds instructions to convert between single-precision 32-bit floating-point values and 16-bit bfloat16 values.
144144
* - ``SPV_INTEL_function_pointers``
145145
- Allows translation of function pointers.
146+
* - ``SPV_INTEL_inline_assembly``
147+
- Allows to use inline assembly.
146148
* - ``SPV_INTEL_optnone``
147149
- Adds OptNoneINTEL value for Function Control mask that indicates a request to not optimize the function.
148150
* - ``SPV_INTEL_subgroups``
@@ -333,6 +335,10 @@ SPIR-V backend, along with their descriptions and argument details.
333335
- 32-bit Integer
334336
- `[]`
335337
- Generates an undefined value. Useful for optimizations and indicating uninitialized variables.
338+
* - `int_spv_inline_asm`
339+
- None
340+
- `[Metadata, Metadata, Vararg]`
341+
- 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.
336342
* - `int_spv_assume`
337343
- None
338344
- `[1-bit Integer]`

llvm/include/llvm/IR/IntrinsicsSPIRV.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ let TargetPrefix = "spv" in {
3636
def int_spv_alloca : Intrinsic<[llvm_any_ty], []>;
3737
def int_spv_alloca_array : Intrinsic<[llvm_any_ty], [llvm_anyint_ty]>;
3838
def int_spv_undef : Intrinsic<[llvm_i32_ty], []>;
39+
def int_spv_inline_asm : Intrinsic<[], [llvm_metadata_ty, llvm_metadata_ty, llvm_vararg_ty]>;
3940

4041
// Expect, Assume Intrinsics
4142
def int_spv_assume : Intrinsic<[], [llvm_i1_ty]>;

llvm/lib/Target/SPIRV/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ add_llvm_target(SPIRVCodeGen
1717
SPIRVAsmPrinter.cpp
1818
SPIRVBuiltins.cpp
1919
SPIRVCallLowering.cpp
20+
SPIRVInlineAsmLowering.cpp
2021
SPIRVCommandLine.cpp
2122
SPIRVDuplicatesTracker.cpp
2223
SPIRVEmitIntrinsics.cpp

llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,14 +321,19 @@ void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
321321
if (MI->getOperand(StrStartIndex).isReg())
322322
break;
323323

324-
std::string Str = getSPIRVStringOperand(*MI, OpNo);
324+
std::string Str = getSPIRVStringOperand(*MI, StrStartIndex);
325325
if (StrStartIndex != OpNo)
326326
O << ' '; // Add a space if we're starting a new string/argument.
327327
O << '"';
328328
for (char c : Str) {
329-
if (c == '"')
330-
O.write('\\'); // Escape " characters (might break for complex UTF-8).
331-
O.write(c);
329+
// Escape ", \n characters (might break for complex UTF-8).
330+
if (c == '\n') {
331+
O.write("\\n", 2);
332+
} else {
333+
if (c == '"')
334+
O.write('\\');
335+
O.write(c);
336+
}
332337
}
333338
O << '"';
334339

llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ static const std::map<std::string, SPIRV::Extension::Extension>
4747
SPIRV::Extension::Extension::SPV_KHR_bit_instructions},
4848
{"SPV_KHR_linkonce_odr",
4949
SPIRV::Extension::Extension::SPV_KHR_linkonce_odr},
50+
{"SPV_INTEL_inline_assembly",
51+
SPIRV::Extension::Extension::SPV_INTEL_inline_assembly},
5052
{"SPV_INTEL_bfloat16_conversion",
5153
SPIRV::Extension::Extension::SPV_INTEL_bfloat16_conversion},
5254
{"SPV_KHR_subgroup_rotate",

llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class SPIRVEmitIntrinsics
140140
Instruction *visitAllocaInst(AllocaInst &I);
141141
Instruction *visitAtomicCmpXchgInst(AtomicCmpXchgInst &I);
142142
Instruction *visitUnreachableInst(UnreachableInst &I);
143+
Instruction *visitCallInst(CallInst &I);
143144

144145
StringRef getPassName() const override { return "SPIRV emit intrinsics"; }
145146

@@ -629,6 +630,28 @@ void SPIRVEmitIntrinsics::preprocessCompositeConstants(IRBuilder<> &B) {
629630
}
630631
}
631632

633+
Instruction *SPIRVEmitIntrinsics::visitCallInst(CallInst &Call) {
634+
if (!Call.isInlineAsm())
635+
return &Call;
636+
637+
const InlineAsm *IA = cast<InlineAsm>(Call.getCalledOperand());
638+
LLVMContext &Ctx = F->getContext();
639+
640+
Constant *TyC = UndefValue::get(IA->getFunctionType());
641+
MDString *ConstraintString = MDString::get(Ctx, IA->getConstraintString());
642+
SmallVector<Value *> Args = {
643+
MetadataAsValue::get(Ctx,
644+
MDNode::get(Ctx, ValueAsMetadata::getConstant(TyC))),
645+
MetadataAsValue::get(Ctx, MDNode::get(Ctx, ConstraintString))};
646+
for (unsigned OpIdx = 0; OpIdx < Call.arg_size(); OpIdx++)
647+
Args.push_back(Call.getArgOperand(OpIdx));
648+
649+
IRBuilder<> B(Call.getParent());
650+
B.SetInsertPoint(&Call);
651+
B.CreateIntrinsic(Intrinsic::spv_inline_asm, {}, {Args});
652+
return &Call;
653+
}
654+
632655
Instruction *SPIRVEmitIntrinsics::visitSwitchInst(SwitchInst &I) {
633656
BasicBlock *ParentBB = I.getParent();
634657
IRBuilder<> B(ParentBB);

llvm/lib/Target/SPIRV/SPIRVISelLowering.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,28 @@ bool SPIRVTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info,
8282
return false;
8383
}
8484

85+
std::pair<unsigned, const TargetRegisterClass *>
86+
SPIRVTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
87+
StringRef Constraint,
88+
MVT VT) const {
89+
const TargetRegisterClass *RC = nullptr;
90+
if (Constraint.starts_with("{"))
91+
return std::make_pair(0u, RC);
92+
93+
if (VT.isFloatingPoint())
94+
RC = VT.isVector() ? &SPIRV::vfIDRegClass
95+
: (VT.getScalarSizeInBits() > 32 ? &SPIRV::fID64RegClass
96+
: &SPIRV::fIDRegClass);
97+
else if (VT.isInteger())
98+
RC = VT.isVector() ? &SPIRV::vIDRegClass
99+
: (VT.getScalarSizeInBits() > 32 ? &SPIRV::ID64RegClass
100+
: &SPIRV::IDRegClass);
101+
else
102+
RC = &SPIRV::IDRegClass;
103+
104+
return std::make_pair(0u, RC);
105+
}
106+
85107
// Insert a bitcast before the instruction to keep SPIR-V code valid
86108
// when there is a type mismatch between results and operand types.
87109
static void validatePtrTypes(const SPIRVSubtarget &STI,

llvm/lib/Target/SPIRV/SPIRVISelLowering.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ class SPIRVTargetLowering : public TargetLowering {
5555
MachineFunction &MF,
5656
unsigned Intrinsic) const override;
5757

58+
std::pair<unsigned, const TargetRegisterClass *>
59+
getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
60+
StringRef Constraint, MVT VT) const override;
61+
unsigned
62+
getNumRegisters(LLVMContext &Context, EVT VT,
63+
std::optional<MVT> RegisterVT = std::nullopt) const override {
64+
return 1;
65+
}
66+
5867
// Call the default implementation and finalize target lowering by inserting
5968
// extra instructions required to preserve validity of SPIR-V code imposed by
6069
// the standard.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===--- SPIRVInlineAsmLowering.cpp - Inline Asm lowering -------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file implements the lowering of LLVM inline asm calls to machine code
10+
// calls for GlobalISel.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "SPIRVInlineAsmLowering.h"
15+
#include "SPIRVSubtarget.h"
16+
#include "llvm/IR/IntrinsicInst.h"
17+
#include "llvm/IR/IntrinsicsSPIRV.h"
18+
19+
using namespace llvm;
20+
21+
SPIRVInlineAsmLowering::SPIRVInlineAsmLowering(const SPIRVTargetLowering &TLI)
22+
: InlineAsmLowering(&TLI) {}
23+
24+
bool SPIRVInlineAsmLowering::lowerAsmOperandForConstraint(
25+
Value *Val, StringRef Constraint, std::vector<MachineOperand> &Ops,
26+
MachineIRBuilder &MIRBuilder) const {
27+
Value *ValOp = nullptr;
28+
if (isa<ConstantInt>(Val)) {
29+
ValOp = Val;
30+
} else if (ConstantFP *CFP = dyn_cast<ConstantFP>(Val)) {
31+
Ops.push_back(MachineOperand::CreateFPImm(CFP));
32+
return true;
33+
} else if (auto *II = dyn_cast<IntrinsicInst>(Val)) {
34+
if (II->getIntrinsicID() == Intrinsic::spv_track_constant) {
35+
if (isa<ConstantInt>(II->getOperand(0))) {
36+
ValOp = II->getOperand(0);
37+
} else if (ConstantFP *CFP = dyn_cast<ConstantFP>(II->getOperand(0))) {
38+
Ops.push_back(MachineOperand::CreateFPImm(CFP));
39+
return true;
40+
}
41+
}
42+
}
43+
return ValOp ? InlineAsmLowering::lowerAsmOperandForConstraint(
44+
ValOp, Constraint, Ops, MIRBuilder)
45+
: false;
46+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//===--- SPIRVInlineAsmLowering.h - Inline Asm lowering ---------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file describes how to lower LLVM inline asm calls to machine
10+
// code calls for GlobalISel.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVINLINEASMLOWERING_H
15+
#define LLVM_LIB_TARGET_SPIRV_SPIRVINLINEASMLOWERING_H
16+
17+
#include "llvm/CodeGen/GlobalISel/InlineAsmLowering.h"
18+
19+
namespace llvm {
20+
21+
class SPIRVTargetLowering;
22+
23+
class SPIRVInlineAsmLowering : public InlineAsmLowering {
24+
public:
25+
SPIRVInlineAsmLowering(const SPIRVTargetLowering &TLI);
26+
bool
27+
lowerAsmOperandForConstraint(Value *Val, StringRef Constraint,
28+
std::vector<MachineOperand> &Ops,
29+
MachineIRBuilder &MIRBuilder) const override;
30+
};
31+
} // end namespace llvm
32+
33+
#endif // LLVM_LIB_TARGET_SPIRV_SPIRVINLINEASMLOWERING_H

0 commit comments

Comments
 (0)