[llvm] [SPIRV] Implement log10 for logical SPIR-V (PR #66921)

Natalie Chouinard via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 27 09:50:24 PDT 2023


https://github.com/sudonatalie updated https://github.com/llvm/llvm-project/pull/66921

>From df0cb92c4f5ee225ddc16295139971a5a2b75396 Mon Sep 17 00:00:00 2001
From: Natalie Chouinard <chouinard at google.com>
Date: Wed, 20 Sep 2023 15:18:38 +0000
Subject: [PATCH 1/4] [SPIRV] Implement log10 for logical SPIR-V

There is no log10 instruction in the GLSL Extended Instruction Set so to
implement the HLSL log10 intrinsic when targeting Vulkan this change
adds the logic to derive the result using the following formula:

```
log10(x) = log2(x) * (1 / log2(10))
         = log2(x) * 0.30103
```
---
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 14 ++++-
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h   |  2 +-
 .../Target/SPIRV/SPIRVInstructionSelector.cpp | 56 ++++++++++++++++++-
 llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp  |  3 +-
 .../CodeGen/SPIRV/hlsl-intrinsics/log10.ll    | 42 ++++++++++++++
 5 files changed, 111 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll

diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index d68454f26a80282..68547e62abf823e 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -242,7 +242,7 @@ Register SPIRVGlobalRegistry::buildConstantInt(uint64_t Val,
 
 Register SPIRVGlobalRegistry::buildConstantFP(APFloat Val,
                                               MachineIRBuilder &MIRBuilder,
-                                              SPIRVType *SpvType) {
+                                              SPIRVType *SpvType, bool EmitIR) {
   auto &MF = MIRBuilder.getMF();
   const Type *LLVMFPTy;
   if (SpvType) {
@@ -260,8 +260,18 @@ Register SPIRVGlobalRegistry::buildConstantFP(APFloat Val,
     MF.getRegInfo().setRegClass(Res, &SPIRV::IDRegClass);
     assignTypeToVReg(LLVMFPTy, Res, MIRBuilder);
     DT.add(ConstFP, &MF, Res);
-    MIRBuilder.buildFConstant(Res, *ConstFP);
+    if (EmitIR) {
+      MIRBuilder.buildFConstant(Res, *ConstFP);
+    } else {
+      MachineInstrBuilder MIB;
+      assert(SpvType);
+      MIB = MIRBuilder.buildInstr(SPIRV::OpConstantF)
+                .addDef(Res)
+                .addUse(getSPIRVTypeID(SpvType));
+      addNumImm(ConstFP->getValueAPF().bitcastToAPInt(), MIB);
+    }
   }
+
   return Res;
 }
 
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index 88769f84b3e504b..304af2440f95ae5 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -234,7 +234,7 @@ class SPIRVGlobalRegistry {
   Register getOrCreateConstInt(uint64_t Val, MachineInstr &I,
                                SPIRVType *SpvType, const SPIRVInstrInfo &TII);
   Register buildConstantFP(APFloat Val, MachineIRBuilder &MIRBuilder,
-                           SPIRVType *SpvType = nullptr);
+                           SPIRVType *SpvType = nullptr, bool EmitIR = true);
   Register getOrCreateConsIntVector(uint64_t Val, MachineInstr &I,
                                     SPIRVType *SpvType,
                                     const SPIRVInstrInfo &TII);
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 035989f2fe571b2..866bcb3cf5c9dba 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -174,6 +174,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectExtInst(Register ResVReg, const SPIRVType *ResType,
                      MachineInstr &I, const ExtInstList &ExtInsts) const;
 
+  bool selectLog10(Register ResVReg, const SPIRVType *ResType,
+                   MachineInstr &I) const;
+
   Register buildI32Constant(uint32_t Val, MachineInstr &I,
                             const SPIRVType *ResType = nullptr) const;
 
@@ -362,7 +365,7 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg,
   case TargetOpcode::G_FLOG2:
     return selectExtInst(ResVReg, ResType, I, CL::log2, GL::Log2);
   case TargetOpcode::G_FLOG10:
-    return selectExtInst(ResVReg, ResType, I, CL::log10);
+    return selectLog10(ResVReg, ResType, I);
 
   case TargetOpcode::G_FABS:
     return selectExtInst(ResVReg, ResType, I, CL::fabs, GL::FAbs);
@@ -1562,6 +1565,57 @@ bool SPIRVInstructionSelector::selectGlobalValue(
   return Reg.isValid();
 }
 
+bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
+                                           const SPIRVType *ResType,
+                                           MachineInstr &I) const {
+  if (STI.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std)) {
+    return selectExtInst(ResVReg, ResType, I, CL::log10);
+  }
+
+  // There is no log10 instruction in the GLSL Extended Instruction set, so it
+  // is implemented as:
+  // log10(x) = log2(x) * (1 / log2(10))
+  //          = log2(x) * 0.30103
+
+  MachineIRBuilder MIRBuilder(I);
+  MachineBasicBlock &BB = *I.getParent();
+
+  // Build log2(x).
+  Register VarReg = MRI->createVirtualRegister(&SPIRV::IDRegClass);
+  bool Result =
+      BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+          .addDef(VarReg)
+          .addUse(GR.getSPIRVTypeID(ResType))
+          .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
+          .addImm(GL::Log)
+          .add(I.getOperand(1))
+          .constrainAllUses(TII, TRI, RBI);
+
+  // Build 0.30103.
+  assert(ResType->getOpcode() == SPIRV::OpTypeVector ||
+         ResType->getOpcode() == SPIRV::OpTypeFloat);
+  // TODO: Add matrix implementeation once supported by the HLSL frontend.
+  const SPIRVType *SpirvScalarType =
+      ResType->getOpcode() == SPIRV::OpTypeVector
+          ? GR.getSPIRVTypeForVReg(ResType->getOperand(1).getReg())
+          : ResType;
+  Register ScaleReg =
+      GR.buildConstantFP(APFloat(0.30103f), MIRBuilder, SpirvScalarType, false);
+
+  // Multiply log2(x) by 0.30103 to get log10(x) result.
+  auto Opcode = ResType->getOpcode() == SPIRV::OpTypeVector
+                    ? SPIRV::OpVectorTimesScalar
+                    : SPIRV::OpFMulS;
+  Result &= BuildMI(BB, I, I.getDebugLoc(), TII.get(Opcode))
+                .addDef(ResVReg)
+                .addUse(GR.getSPIRVTypeID(ResType))
+                .addUse(VarReg)
+                .addUse(ScaleReg)
+                .constrainAllUses(TII, TRI, RBI);
+
+  return Result;
+}
+
 namespace llvm {
 InstructionSelector *
 createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index b0028f8c80a406e..cffa0acd25226c3 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -234,6 +234,7 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
                                G_FEXP2,
                                G_FLOG,
                                G_FLOG2,
+                               G_FLOG10,
                                G_FABS,
                                G_FMINNUM,
                                G_FMAXNUM,
@@ -259,8 +260,6 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
       allFloatScalarsAndVectors, allIntScalarsAndVectors);
 
   if (ST.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std)) {
-    getActionDefinitionsBuilder(G_FLOG10).legalFor(allFloatScalarsAndVectors);
-
     getActionDefinitionsBuilder(
         {G_CTTZ, G_CTTZ_ZERO_UNDEF, G_CTLZ, G_CTLZ_ZERO_UNDEF})
         .legalForCartesianProduct(allIntScalarsAndVectors,
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll
new file mode 100644
index 000000000000000..7a3a611f5e7f1d8
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll
@@ -0,0 +1,42 @@
+; RUN: llc -O0 -mtriple=spirv-unknown-linux %s -o - | FileCheck %s
+
+; CHECK: OpExtInstImport "GLSL.std.450"
+
+; CHECK: %[[#float:]] = OpTypeFloat 32
+; CHECK: %[[#v4float:]] = OpTypeVector %[[#float]] 4
+; CHECK: %[[#float_0_30103001:]] = OpConstant %[[#float]] 0.30103000998497009
+; CHECK: %[[#_ptr_Function_v4float:]] = OpTypePointer Function %[[#v4float]]
+; CHECK: %[[#_ptr_Function_float:]] = OpTypePointer Function %[[#float]]
+
+define void @main() {
+entry:
+; CHECK: %[[#f:]] = OpVariable %[[#_ptr_Function_float]] Function
+; CHECK: %[[#logf:]] = OpVariable %[[#_ptr_Function_float]] Function
+; CHECK: %[[#f4:]] = OpVariable %[[#_ptr_Function_v4float]] Function
+; CHECK: %[[#logf4:]] = OpVariable %[[#_ptr_Function_v4float]] Function
+  %f = alloca float, align 4
+  %logf = alloca float, align 4
+  %f4 = alloca <4 x float>, align 16
+  %logf4 = alloca <4 x float>, align 16
+
+; CHECK: %[[#load:]] = OpLoad %[[#float]] %[[#f]] Aligned 4
+; CHECK: %[[#log2:]] = OpExtInst %[[#float]] %15 Log %[[#load]]
+; CHECK: %[[#res:]] = OpFMul %[[#float]] %[[#log2]] %[[#float_0_30103001]]
+; CHECK: OpStore %[[#logf]] %[[#res]] Aligned 4
+  %0 = load float, ptr %f, align 4
+  %elt.log10 = call float @llvm.log10.f32(float %0)
+  store float %elt.log10, ptr %logf, align 4
+
+; CHECK: %[[#load:]] = OpLoad %[[#v4float]] %[[#f4]] Aligned 16
+; CHECK: %[[#log2:]] = OpExtInst %[[#v4float]] %15 Log %[[#load]]
+; CHECK: %[[#res:]] = OpVectorTimesScalar %[[#v4float]] %[[#log2]] %[[#float_0_30103001]]
+; CHECK: OpStore %[[#logf4]] %[[#res]] Aligned 16
+  %1 = load <4 x float>, ptr %f4, align 16
+  %elt.log101 = call <4 x float> @llvm.log10.v4f32(<4 x float> %1)
+  store <4 x float> %elt.log101, ptr %logf4, align 16
+
+  ret void
+}
+
+declare float @llvm.log10.f32(float)
+declare <4 x float> @llvm.log10.v4f32(<4 x float>)

>From 4c7bb09a8ede47464af556ef083439326f8a90ff Mon Sep 17 00:00:00 2001
From: Natalie Chouinard <chouinard at google.com>
Date: Wed, 27 Sep 2023 15:55:43 +0000
Subject: [PATCH 2/4] Use Log2 not Log

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 2 +-
 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll   | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 866bcb3cf5c9dba..bae0a91efa917ce 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1587,7 +1587,7 @@ bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
           .addDef(VarReg)
           .addUse(GR.getSPIRVTypeID(ResType))
           .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
-          .addImm(GL::Log)
+          .addImm(GL::Log2)
           .add(I.getOperand(1))
           .constrainAllUses(TII, TRI, RBI);
 
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll
index 7a3a611f5e7f1d8..e7b00eb962f444e 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/log10.ll
@@ -1,6 +1,6 @@
 ; RUN: llc -O0 -mtriple=spirv-unknown-linux %s -o - | FileCheck %s
 
-; CHECK: OpExtInstImport "GLSL.std.450"
+; CHECK: %[[#extinst:]] = OpExtInstImport "GLSL.std.450"
 
 ; CHECK: %[[#float:]] = OpTypeFloat 32
 ; CHECK: %[[#v4float:]] = OpTypeVector %[[#float]] 4
@@ -20,7 +20,7 @@ entry:
   %logf4 = alloca <4 x float>, align 16
 
 ; CHECK: %[[#load:]] = OpLoad %[[#float]] %[[#f]] Aligned 4
-; CHECK: %[[#log2:]] = OpExtInst %[[#float]] %15 Log %[[#load]]
+; CHECK: %[[#log2:]] = OpExtInst %[[#float]] %[[#extinst]] Log2 %[[#load]]
 ; CHECK: %[[#res:]] = OpFMul %[[#float]] %[[#log2]] %[[#float_0_30103001]]
 ; CHECK: OpStore %[[#logf]] %[[#res]] Aligned 4
   %0 = load float, ptr %f, align 4
@@ -28,7 +28,7 @@ entry:
   store float %elt.log10, ptr %logf, align 4
 
 ; CHECK: %[[#load:]] = OpLoad %[[#v4float]] %[[#f4]] Aligned 16
-; CHECK: %[[#log2:]] = OpExtInst %[[#v4float]] %15 Log %[[#load]]
+; CHECK: %[[#log2:]] = OpExtInst %[[#v4float]] %[[#extinst]] Log2 %[[#load]]
 ; CHECK: %[[#res:]] = OpVectorTimesScalar %[[#v4float]] %[[#log2]] %[[#float_0_30103001]]
 ; CHECK: OpStore %[[#logf4]] %[[#res]] Aligned 16
   %1 = load <4 x float>, ptr %f4, align 16

>From 82950eaac0b767e39fc7649fb58ea2dda4f03dac Mon Sep 17 00:00:00 2001
From: Natalie Chouinard <chouinard at google.com>
Date: Wed, 27 Sep 2023 15:57:02 +0000
Subject: [PATCH 3/4] Correct reg types

---
 llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 68547e62abf823e..c9adf6c86929b35 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -256,9 +256,11 @@ Register SPIRVGlobalRegistry::buildConstantFP(APFloat Val,
   Register Res = DT.find(ConstFP, &MF);
   if (!Res.isValid()) {
     unsigned BitWidth = SpvType ? getScalarOrVectorBitWidth(SpvType) : 32;
-    Res = MF.getRegInfo().createGenericVirtualRegister(LLT::scalar(BitWidth));
+    LLT LLTy = LLT::scalar(EmitIR ? BitWidth : 32);
+    Res = MF.getRegInfo().createGenericVirtualRegister(LLTy);
     MF.getRegInfo().setRegClass(Res, &SPIRV::IDRegClass);
-    assignTypeToVReg(LLVMFPTy, Res, MIRBuilder);
+    assignTypeToVReg(LLVMFPTy, Res, MIRBuilder,
+                     SPIRV::AccessQualifier::ReadWrite, EmitIR);
     DT.add(ConstFP, &MF, Res);
     if (EmitIR) {
       MIRBuilder.buildFConstant(Res, *ConstFP);

>From 3373e006bcbc61c759c02d876ab0852483fcf908 Mon Sep 17 00:00:00 2001
From: Natalie Chouinard <chouinard at google.com>
Date: Wed, 27 Sep 2023 16:38:55 +0000
Subject: [PATCH 4/4] Typo and TODO

---
 llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp | 2 +-
 llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp       | 4 ++++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index bae0a91efa917ce..d0dcc4fd4acbf26 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -1594,7 +1594,7 @@ bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
   // Build 0.30103.
   assert(ResType->getOpcode() == SPIRV::OpTypeVector ||
          ResType->getOpcode() == SPIRV::OpTypeFloat);
-  // TODO: Add matrix implementeation once supported by the HLSL frontend.
+  // TODO: Add matrix implementation once supported by the HLSL frontend.
   const SPIRVType *SpirvScalarType =
       ResType->getOpcode() == SPIRV::OpTypeVector
           ? GR.getSPIRVTypeForVReg(ResType->getOperand(1).getReg())
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index cffa0acd25226c3..faaf7f0e2548910 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -229,6 +229,10 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
   // Control-flow. In some cases (e.g. constants) s1 may be promoted to s32.
   getActionDefinitionsBuilder(G_BRCOND).legalFor({s1, s32});
 
+  // TODO: Review the target OpenCL and GLSL Extended Instruction Set specs to
+  // tighten these requirements. Many of these math functions are only legal on
+  // specific bitwidths, so they are not selectable for
+  // allFloatScalarsAndVectors.
   getActionDefinitionsBuilder({G_FPOW,
                                G_FEXP,
                                G_FEXP2,



More information about the llvm-commits mailing list