Files
clang-p2996/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.cpp
Vyacheslav Levytskyy ed22029eea [SPIR-V] Address the case when optimization uses GEP operator and GenCode creates G_PTR_ADD to convey the semantics (#107880)
When running SPIR-V Backend with optimization levels higher than 0, we
observe GEP Operator's as a new factor, massively used to convey the
semantics of the original LLVM IR. Previously, an issue related to GEP
Operator was mentioned and fixed on the consumer side of toolchains
(see, for example, Khronos Trandslator Issue
https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/2486 and PR
https://github.com/KhronosGroup/SPIRV-LLVM-Translator/pull/2487).
However, there is a case when GenCode creates G_PTR_ADD to convey the
original semantics under optimization levels higher than 0 where it's
SPIR-V Backend that fails to translate source LLVM IR correctly.

Consider the following reproducer:

```
%struct = type { i32, [257 x i8], [257 x i8], [129 x i8], i32, i64, i64, i64, i64, i64, i64 }
@Mem = linkonce_odr dso_local addrspace(1) global %struct zeroinitializer, align 8

define weak dso_local spir_func void @__devicelib_assert_fail(ptr addrspace(4) noundef %expr, i32 noundef %line, i1 %fl) {
entry:
  %cmp = icmp eq i32 %line, 0
  br i1 %cmp, label %lbl, label %exit

lbl:
  store i32 %line, ptr addrspace(1) getelementptr inbounds (i8, ptr addrspace(1) @Mem, i64 648), align 8
  br i1 %fl, label %lbl, label %exit

exit:
  ret void
}
```

converted to the following machine instructions by SPIR-V Backend:

```
  %4:type(s64) = OpTypeInt 32, 0
  %22:type(s64) = OpTypePointer 5, %4:type(s64)
  %2:type(s64) = OpTypeInt 8, 0
  %28:type(s64) = OpTypePointer 5, %2:type(s64)

  %10:pid(p1) = G_GLOBAL_VALUE @Mem

  %36:type(s64) = OpTypeStruct %4:type(s64), %32:type(s64), %32:type(s64), %34:type(s64), %4:type(s64), %35:type(s64), %35:type(s64), %35:type(s64), %35:type(s64), %35:type(s64), %35:type(s64)
  %37:iid(s32) = G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.const.composite)

  %8:iid(s32) = ASSIGN_TYPE %37:iid(s32), %36:type(s64)
  G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.spv.init.global), %10:pid(p1), %8:iid(s32)

  %29:pid(p1) = nuw G_PTR_ADD %10:pid, %16:iid(s64)
  %15:pid(p1) = nuw ASSIGN_TYPE %29:pid(p1), %28:type(s64)

  %27:pid(p2) = G_BITCAST %15:pid(p1)
  %17:pid(p2) = ASSIGN_TYPE %27:pid(p2), %22:type(s64)
  G_STORE %1:iid(s32), %17:pid(p2) :: (store (s32) into %ir.3, align 8, addrspace 1)
```

On the next stage of instruction selection this `G_PTR_ADD`-related
pattern would be interpreted as an initialization of a global variable
and converted to an invalid constant GEP pattern that, in its turn,
would fail to be verified by LLVM during back translation from SPIR-V to
LLVM IR.

This PR introduces a fix for the problem by adding one more case of
`G_PTR_ADD` translation, when we use a non-const GEP to convey the
meaning. The reproducer is attached as a new test case.
2024-09-11 14:18:14 +02:00

121 lines
4.3 KiB
C++

//===-- SPIRVDuplicatesTracker.cpp - SPIR-V Duplicates Tracker --*- 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
//
//===----------------------------------------------------------------------===//
//
// General infrastructure for keeping track of the values that according to
// the SPIR-V binary layout should be global to the whole module.
//
//===----------------------------------------------------------------------===//
#include "SPIRVDuplicatesTracker.h"
#define DEBUG_TYPE "build-dep-graph"
using namespace llvm;
template <typename T>
void SPIRVGeneralDuplicatesTracker::prebuildReg2Entry(
SPIRVDuplicatesTracker<T> &DT, SPIRVReg2EntryTy &Reg2Entry) {
for (auto &TPair : DT.getAllUses()) {
for (auto &RegPair : TPair.second) {
const MachineFunction *MF = RegPair.first;
Register R = RegPair.second;
MachineInstr *MI = MF->getRegInfo().getUniqueVRegDef(R);
if (!MI)
continue;
Reg2Entry[&MI->getOperand(0)] = &TPair.second;
}
}
}
void SPIRVGeneralDuplicatesTracker::buildDepsGraph(
std::vector<SPIRV::DTSortableEntry *> &Graph,
MachineModuleInfo *MMI = nullptr) {
SPIRVReg2EntryTy Reg2Entry;
prebuildReg2Entry(TT, Reg2Entry);
prebuildReg2Entry(CT, Reg2Entry);
prebuildReg2Entry(GT, Reg2Entry);
prebuildReg2Entry(FT, Reg2Entry);
prebuildReg2Entry(AT, Reg2Entry);
prebuildReg2Entry(MT, Reg2Entry);
prebuildReg2Entry(ST, Reg2Entry);
for (auto &Op2E : Reg2Entry) {
SPIRV::DTSortableEntry *E = Op2E.second;
Graph.push_back(E);
for (auto &U : *E) {
const MachineRegisterInfo &MRI = U.first->getRegInfo();
MachineInstr *MI = MRI.getUniqueVRegDef(U.second);
if (!MI)
continue;
assert(MI && MI->getParent() && "No MachineInstr created yet");
for (auto i = MI->getNumDefs(); i < MI->getNumOperands(); i++) {
MachineOperand &Op = MI->getOperand(i);
if (!Op.isReg())
continue;
MachineInstr *VRegDef = MRI.getVRegDef(Op.getReg());
// References to a function via function pointers generate virtual
// registers without a definition. We are able to resolve this
// reference using Globar Register info into an OpFunction instruction
// but do not expect to find it in Reg2Entry.
if (MI->getOpcode() == SPIRV::OpConstantFunctionPointerINTEL && i == 2)
continue;
MachineOperand *RegOp = &VRegDef->getOperand(0);
LLVM_DEBUG({
if (Reg2Entry.count(RegOp) == 0 &&
(MI->getOpcode() != SPIRV::OpVariable || i != 3)) {
dbgs() << "Unexpected pattern while building a dependency "
"graph.\nInstruction: ";
MI->print(dbgs());
dbgs() << "Operand: ";
Op.print(dbgs());
dbgs() << "\nOperand definition: ";
VRegDef->print(dbgs());
}
});
assert((MI->getOpcode() == SPIRV::OpVariable && i == 3) ||
Reg2Entry.count(RegOp));
if (Reg2Entry.count(RegOp))
E->addDep(Reg2Entry[RegOp]);
}
if (E->getIsFunc()) {
MachineInstr *Next = MI->getNextNode();
if (Next && (Next->getOpcode() == SPIRV::OpFunction ||
Next->getOpcode() == SPIRV::OpFunctionParameter)) {
E->addDep(Reg2Entry[&Next->getOperand(0)]);
}
}
}
}
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
if (MMI) {
const Module *M = MMI->getModule();
for (auto F = M->begin(), E = M->end(); F != E; ++F) {
const MachineFunction *MF = MMI->getMachineFunction(*F);
if (!MF)
continue;
for (const MachineBasicBlock &MBB : *MF) {
for (const MachineInstr &CMI : MBB) {
MachineInstr &MI = const_cast<MachineInstr &>(CMI);
MI.dump();
if (MI.getNumExplicitDefs() > 0 &&
Reg2Entry.count(&MI.getOperand(0))) {
dbgs() << "\t[";
for (SPIRV::DTSortableEntry *D :
Reg2Entry.lookup(&MI.getOperand(0))->getDeps())
dbgs() << Register::virtReg2Index(D->lookup(MF)) << ", ";
dbgs() << "]\n";
}
}
}
}
}
#endif
}