-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[SPIRV] Add PreLegalizer pattern matching for faceforward
#139959
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
base: main
Are you sure you want to change the base?
Changes from all commits
be7df59
a9459be
28c3910
e6dfc5b
5cb68c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,6 +50,18 @@ namespace { | |
#include "SPIRVGenPreLegalizeGICombiner.inc" | ||
#undef GET_GICOMBINER_TYPES | ||
|
||
static void removeAllUses(Register Reg, MachineRegisterInfo &MRI, | ||
SPIRVGlobalRegistry *GR) { | ||
SmallVector<MachineInstr *, 4> UsesToErase( | ||
llvm::make_pointer_range(MRI.use_instructions(Reg))); | ||
|
||
// calling eraseFromParent too early invalidates the iterator. | ||
for (auto *MIToErase : UsesToErase) { | ||
GR->invalidateMachineInstr(MIToErase); | ||
MIToErase->eraseFromParent(); | ||
} | ||
} | ||
|
||
/// This match is part of a combine that | ||
/// rewrites length(X - Y) to distance(X, Y) | ||
/// (f32 (g_intrinsic length | ||
|
@@ -98,21 +110,110 @@ void applySPIRVDistance(MachineInstr &MI, MachineRegisterInfo &MRI, | |
|
||
SPIRVGlobalRegistry *GR = | ||
MI.getMF()->getSubtarget<SPIRVSubtarget>().getSPIRVGlobalRegistry(); | ||
auto RemoveAllUses = [&](Register Reg) { | ||
SmallVector<MachineInstr *, 4> UsesToErase( | ||
llvm::make_pointer_range(MRI.use_instructions(Reg))); | ||
|
||
// calling eraseFromParent to early invalidates the iterator. | ||
for (auto *MIToErase : UsesToErase) { | ||
GR->invalidateMachineInstr(MIToErase); | ||
MIToErase->eraseFromParent(); | ||
} | ||
}; | ||
RemoveAllUses(SubDestReg); // remove all uses of FSUB Result | ||
removeAllUses(SubDestReg, MRI, GR); // remove all uses of FSUB Result | ||
GR->invalidateMachineInstr(SubInstr); | ||
SubInstr->eraseFromParent(); // remove FSUB instruction | ||
} | ||
|
||
/// This match is part of a combine that | ||
/// rewrites select(fcmp(dot(I, Ng), 0), N, 0 - N) to faceforward(N, I, Ng) | ||
/// (vXf32 (g_select | ||
/// (g_fcmp | ||
/// (g_intrinsic dot(vXf32 I) (vXf32 Ng) | ||
/// 0) | ||
/// (vXf32 N) | ||
/// (vXf32 g_fsub (0) (vXf32 N)))) | ||
/// -> | ||
/// (vXf32 (g_intrinsic faceforward | ||
/// (vXf32 N) (vXf32 I) (vXf32 Ng))) | ||
/// | ||
/// This only works for Vulkan targets. | ||
/// | ||
bool matchSelectToFaceForward(MachineInstr &MI, MachineRegisterInfo &MRI) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be done in tablegen patterns, or at least use mi_match? |
||
const SPIRVSubtarget &ST = MI.getMF()->getSubtarget<SPIRVSubtarget>(); | ||
if (!ST.isVulkanEnv()) | ||
return false; | ||
|
||
if (MI.getOpcode() != TargetOpcode::G_SELECT) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add something to the match to make sure we are targeting vulkan? You will need to add a test case for openCL like so:
Ie confirm we don't do a pattern replacement. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was trying to write this openCL test but ran into this issue for Through manual testing, the check I added for vulkan seems to work. |
||
return false; | ||
|
||
// Check if select's condition is a comparison between a dot product and 0. | ||
Register CondReg = MI.getOperand(1).getReg(); | ||
MachineInstr *CondInstr = MRI.getVRegDef(CondReg); | ||
if (!CondInstr || CondInstr->getOpcode() != TargetOpcode::G_FCMP) | ||
return false; | ||
|
||
Register DotReg = CondInstr->getOperand(2).getReg(); | ||
MachineInstr *DotInstr = MRI.getVRegDef(DotReg); | ||
if ((DotInstr->getOpcode() != TargetOpcode::G_INTRINSIC || | ||
cast<GIntrinsic>(DotInstr)->getIntrinsicID() != Intrinsic::spv_fdot)) { | ||
if (DotInstr->getOpcode() != TargetOpcode::G_FMUL) | ||
return false; | ||
Register DotOperand1 = DotInstr->getOperand(1).getReg(); | ||
Register DotOperand2 = DotInstr->getOperand(2).getReg(); | ||
if (MRI.getType(DotOperand1).isVector() || | ||
MRI.getType(DotOperand2).isVector()) | ||
return false; | ||
} | ||
|
||
Register CondZeroReg = CondInstr->getOperand(3).getReg(); | ||
MachineInstr *CondZeroInstr = MRI.getVRegDef(CondZeroReg); | ||
if (CondZeroInstr->getOpcode() != TargetOpcode::G_FCONSTANT || | ||
!CondZeroInstr->getOperand(1).getFPImm()->isZero()) | ||
return false; | ||
|
||
// Check if select's false operand is the negation of the true operand. | ||
Register TrueReg = MI.getOperand(2).getReg(); | ||
Register FalseReg = MI.getOperand(3).getReg(); | ||
MachineInstr *FalseInstr = MRI.getVRegDef(FalseReg); | ||
if (FalseInstr->getOpcode() != TargetOpcode::G_FNEG) | ||
return false; | ||
if (TrueReg != FalseInstr->getOperand(1).getReg()) | ||
return false; | ||
Comment on lines
+166
to
+172
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible that the operands be constants like I don't know llvm well enough. Is there a generic way to check if one value is the negation of the other? In other compilers I worked on, you could create a temporary express (a + b) and check if the simplifies folds it to 0. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I was also wondering if this way of checking would be a problem since there's multiple ways to represent 2 operands being negations (another example being |
||
|
||
return true; | ||
} | ||
void applySPIRVFaceForward(MachineInstr &MI, MachineRegisterInfo &MRI, | ||
MachineIRBuilder &B) { | ||
|
||
// Extract the operands for N, I, and Ng from the match criteria. | ||
Register CondReg = MI.getOperand(1).getReg(); | ||
MachineInstr *CondInstr = MRI.getVRegDef(CondReg); | ||
Register DotReg = CondInstr->getOperand(2).getReg(); | ||
MachineInstr *DotInstr = MRI.getVRegDef(DotReg); | ||
Register DotOperand1, DotOperand2; | ||
if (DotInstr->getOpcode() == TargetOpcode::G_FMUL) { | ||
DotOperand1 = DotInstr->getOperand(1).getReg(); | ||
DotOperand2 = DotInstr->getOperand(2).getReg(); | ||
} else { | ||
DotOperand1 = DotInstr->getOperand(2).getReg(); | ||
DotOperand2 = DotInstr->getOperand(3).getReg(); | ||
} | ||
Register TrueReg = MI.getOperand(2).getReg(); | ||
|
||
// Remove the original `select` instruction. | ||
Register ResultReg = MI.getOperand(0).getReg(); | ||
DebugLoc DL = MI.getDebugLoc(); | ||
MachineBasicBlock &MBB = *MI.getParent(); | ||
MachineBasicBlock::iterator InsertPt = MI.getIterator(); | ||
|
||
// Build the `spv_faceforward` intrinsic. | ||
MachineInstrBuilder NewInstr = | ||
BuildMI(MBB, InsertPt, DL, B.getTII().get(TargetOpcode::G_INTRINSIC)); | ||
NewInstr | ||
.addDef(ResultReg) // Result register | ||
.addIntrinsicID(Intrinsic::spv_faceforward) // Intrinsic ID | ||
.addUse(TrueReg) // Operand N | ||
.addUse(DotOperand1) // Operand I | ||
.addUse(DotOperand2); // Operand Ng | ||
|
||
SPIRVGlobalRegistry *GR = | ||
MI.getMF()->getSubtarget<SPIRVSubtarget>().getSPIRVGlobalRegistry(); | ||
removeAllUses(CondReg, MRI, GR); // remove all uses of FCMP Result | ||
GR->invalidateMachineInstr(CondInstr); | ||
CondInstr->eraseFromParent(); // remove FCMP instruction | ||
} | ||
|
||
class SPIRVPreLegalizerCombinerImpl : public Combiner { | ||
protected: | ||
const CombinerHelper Helper; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# RUN: llc -verify-machineinstrs -O0 -mtriple spirv-unknown-unknown -run-pass=spirv-prelegalizer-combiner %s -o - | FileCheck %s | ||
# REQUIRES: asserts | ||
--- | ||
name: faceforward_instcombine_float | ||
tracksRegLiveness: true | ||
legalized: true | ||
body: | | ||
bb.1.entry: | ||
; CHECK-LABEL: name: faceforward_instcombine_float | ||
; CHECK-NOT: %9:_(s32) = G_FCONSTANT float 0.000000e+00 | ||
; CHECK-NOT: %8:_(s32) = G_FMUL %1:fid, %2:fid | ||
; CHECK-NOT: %10:_(s1) = G_FCMP floatpred(olt), %8:_(s32), %9:_ | ||
; CHECK-NOT: %11:_(s32) = G_FNEG %0:fid | ||
; CHECK-NOT: %12:id(s32) = G_SELECT %10:_(s1), %0:fid, %11:_ | ||
; CHECK: %10:id(s32) = G_INTRINSIC intrinsic(@llvm.spv.faceforward), %2(s32), %3(s32), %4(s32) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably my fault, I don't think I did enought in the distance ticket to test it properly. But in length to distance we were replacing one intrinsic with another so I think it reuses the intrinsic side effects. In this case we are creating an intrinsic out of nothing should we be wrapping the intrinsic in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really familiar with what There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You shouldn't need to introduce side effects out of nothing (and the G_INTRINSIC* opcode used should be consistent with the defined attributes for the underlying intrinsic)
kmpeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
; CHECK: OpReturnValue %10(s32) | ||
%3:type(s64) = OpTypeFloat 32 | ||
%5:type(s64) = OpTypeFunction %3:type(s64), %3:type(s64), %3:type(s64), %3:type(s64) | ||
OpName %0:fid(s32), 97 | ||
OpName %1:fid(s32), 98 | ||
OpName %2:fid(s32), 99 | ||
%4:iid(s64) = OpFunction %3:type(s64), 0, %5:type(s64) | ||
%0:fid(s32) = OpFunctionParameter %3:type(s64) | ||
%1:fid(s32) = OpFunctionParameter %3:type(s64) | ||
%2:fid(s32) = OpFunctionParameter %3:type(s64) | ||
OpName %4:iid(s64), 1701011814, 2003988326, 1600418401, 1953721961, 1651339107, 1600482921, 1634692198, 116 | ||
%9:_(s32) = G_FCONSTANT float 0.000000e+00 | ||
%8:_(s32) = G_FMUL %1:fid, %2:fid | ||
%10:_(s1) = G_FCMP floatpred(olt), %8:_(s32), %9:_ | ||
%11:_(s32) = G_FNEG %0:fid | ||
%12:id(s32) = G_SELECT %10:_(s1), %0:fid, %11:_ | ||
OpReturnValue %12:id(s32) | ||
--- | ||
name: faceforward_instcombine_float4 | ||
tracksRegLiveness: true | ||
legalized: true | ||
body: | | ||
bb.1.entry: | ||
; CHECK-LABEL: name: faceforward_instcombine_float4 | ||
; CHECK-NOT: %10:_(s32) = G_FCONSTANT float 0.000000e+00 | ||
; CHECK-NOT: %9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>) | ||
; CHECK-NOT: %11:_(s1) = G_FCMP floatpred(olt), %9:_(s32), %10:_ | ||
; CHECK-NOT: %12:_(<4 x s32>) = G_FNEG %0:vfid | ||
; CHECK-NOT: %13:id(<4 x s32>) = G_SELECT %11:_(s1), %0:vfid, %12:_ | ||
; CHECK: %11:id(<4 x s32>) = G_INTRINSIC intrinsic(@llvm.spv.faceforward), %3(<4 x s32>), %4(<4 x s32>), %5(<4 x s32>) | ||
; CHECK: OpReturnValue %11(<4 x s32>) | ||
%4:type(s64) = OpTypeVector %3:type(s64), 4 | ||
%6:type(s64) = OpTypeFunction %4:type(s64), %4:type(s64), %4:type(s64), %4:type(s64) | ||
%3:type(s64) = OpTypeFloat 32 | ||
OpName %0:vfid(<4 x s32>), 97 | ||
OpName %1:vfid(<4 x s32>), 98 | ||
OpName %2:vfid(<4 x s32>), 99 | ||
%5:iid(s64) = OpFunction %4:type(s64), 0, %6:type(s64) | ||
%0:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64) | ||
%1:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64) | ||
%2:vfid(<4 x s32>) = OpFunctionParameter %4:type(s64) | ||
OpName %5:iid(s64), 1701011814, 2003988326, 1600418401, 1953721961, 1651339107, 1600482921, 1634692198, 13428 | ||
OpDecorate %5:iid(s64), 41, 1701011814, 2003988326, 1600418401, 1953721961, 1651339107, 1600482921, 1634692198, 13428, 0 | ||
%10:_(s32) = G_FCONSTANT float 0.000000e+00 | ||
%9:_(s32) = G_INTRINSIC intrinsic(@llvm.spv.fdot), %1:vfid(<4 x s32>), %2:vfid(<4 x s32>) | ||
%11:_(s1) = G_FCMP floatpred(olt), %9:_(s32), %10:_ | ||
%12:_(<4 x s32>) = G_FNEG %0:vfid | ||
%13:id(<4 x s32>) = G_SELECT %11:_(s1), %0:vfid, %12:_ | ||
OpReturnValue %13:id(<4 x s32>) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not be done in this patch