SpirvShader: Implement GLSLstd450Frexp
Bug: b/126873455
Tests: dEQP-VK.glsl.builtin.function.common.frexp.*
Change-Id: Ic725c53191c3a6ec6494859d6eb8e6a8eb77c90e
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/28628
Tested-by: Ben Clayton <bclayton@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 8a8077a..0c1f43d 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -3179,6 +3179,48 @@
dst.move(1, HalfToFloatBits((val.UInt(0) & SIMD::UInt(0xFFFF0000)) >> 16));
break;
}
+ case GLSLstd450Frexp:
+ {
+ auto val = GenericValue(this, routine, insn.word(5));
+ auto ptrId = Object::ID(insn.word(6));
+ auto ptrTy = getType(getObject(ptrId).type);
+ auto ptr = GetPointerToData(ptrId, 0, routine);
+ bool interleavedByLane = IsStorageInterleavedByLane(ptrTy.storageClass);
+
+ for (auto i = 0u; i < type.sizeInComponents; i++)
+ {
+ SIMD::Float significand;
+ SIMD::Int exponent;
+ std::tie(significand, exponent) = Frexp(val.Float(i));
+
+ dst.move(i, significand);
+
+ // TODO: Refactor and consolidate with EmitStore.
+ for (int j = 0; j < SIMD::Width; j++)
+ {
+ auto ptrBase = Pointer<Int>(ptr.base);
+ If(Extract(state->activeLaneMask(), j) != 0)
+ {
+ Int offset = Int(i) + Extract(ptr.offset, j);
+ if (interleavedByLane) { offset = offset * SIMD::Width + j; }
+ Store(Extract(exponent, j), &ptrBase[offset], sizeof(uint32_t), false, std::memory_order_relaxed);
+ }
+ }
+ }
+ break;
+ }
+ case GLSLstd450FrexpStruct:
+ {
+ auto val = GenericValue(this, routine, insn.word(5));
+ auto numComponents = getType(val.type).sizeInComponents;
+ for (auto i = 0u; i < numComponents; i++)
+ {
+ auto significandAndExponent = Frexp(val.Float(i));
+ dst.move(i, significandAndExponent.first);
+ dst.move(i + numComponents, significandAndExponent.second);
+ }
+ break;
+ }
default:
UNIMPLEMENTED("Unhandled ExtInst %d", extInstIndex);
}
@@ -3250,6 +3292,17 @@
(CmpNLE(As<SIMD::UInt>(expmant), SIMD::UInt(was_infnan)) & SIMD::UInt(exp_infnan));
}
+ std::pair<SIMD::Float, SIMD::Int> SpirvShader::Frexp(RValue<SIMD::Float> val) const
+ {
+ // Assumes IEEE 754
+ auto v = As<SIMD::UInt>(val);
+ auto isNotZero = CmpNEQ(v & SIMD::UInt(0x7FFFFFFF), SIMD::UInt(0));
+ auto zeroSign = v & SIMD::UInt(0x80000000) & ~isNotZero;
+ auto significand = As<SIMD::Float>((v & SIMD::UInt(0x807FFFFF) | SIMD::UInt(0x3F000000)) & isNotZero | zeroSign);
+ auto exponent = (SIMD::Int((v >> SIMD::UInt(23)) & SIMD::UInt(0xFF)) - SIMD::Int(126)) & SIMD::Int(isNotZero);
+ return std::make_pair(significand, exponent);
+ }
+
SpirvShader::EmitResult SpirvShader::EmitAny(InsnIterator insn, EmitState *state) const
{
auto routine = state->routine;
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 494ea6b..6254af6 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -691,8 +691,15 @@
// Helper as we often need to take dot products as part of doing other things.
SIMD::Float Dot(unsigned numComponents, GenericValue const & x, GenericValue const & y) const;
+
SIMD::UInt FloatToHalfBits(SIMD::UInt floatBits, bool storeInUpperBits) const;
SIMD::UInt HalfToFloatBits(SIMD::UInt halfBits) const;
+
+ // Splits x into a floating-point significand in the range [0.5, 1.0)
+ // and an integral exponent of two, such that:
+ // x = significand * 2^exponent
+ // Returns the pair <significand, exponent>
+ std::pair<SIMD::Float, SIMD::Int> Frexp(RValue<SIMD::Float> val) const;
};
class SpirvRoutine
diff --git a/src/Reactor/Reactor.cpp b/src/Reactor/Reactor.cpp
index 80d12e0..96a8d25 100644
--- a/src/Reactor/Reactor.cpp
+++ b/src/Reactor/Reactor.cpp
@@ -3572,6 +3572,16 @@
return RValue<UInt4>(Nucleus::createNot(val.value));
}
+ RValue<UInt> Extract(RValue<UInt4> x, int i)
+ {
+ return RValue<UInt>(Nucleus::createExtractElement(x.value, Int::getType(), i));
+ }
+
+ RValue<UInt4> Insert(RValue<UInt4> x, RValue<UInt> element, int i)
+ {
+ return RValue<UInt4>(Nucleus::createInsertElement(x.value, element.value, i));
+ }
+
Half::Half(RValue<Float> cast)
{
UInt fp32i = As<UInt>(cast);
diff --git a/src/Reactor/Reactor.hpp b/src/Reactor/Reactor.hpp
index 93613b2..bb32a46 100644
--- a/src/Reactor/Reactor.hpp
+++ b/src/Reactor/Reactor.hpp
@@ -1972,6 +1972,8 @@
RValue<UInt4> Max(RValue<UInt4> x, RValue<UInt4> y);
RValue<UInt4> Min(RValue<UInt4> x, RValue<UInt4> y);
RValue<UInt4> MulHigh(RValue<UInt4> x, RValue<UInt4> y);
+ RValue<UInt> Extract(RValue<UInt4> val, int i);
+ RValue<UInt4> Insert(RValue<UInt4> val, RValue<UInt> element, int i);
// RValue<UInt4> RoundInt(RValue<Float4> cast);
class Half : public LValue<Half>