| // Copyright 2019 The SwiftShader Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "SpirvShader.hpp" |
| |
| #include <spirv/unified1/spirv.hpp> |
| |
| namespace sw { |
| |
| void SpirvShader::EvalSpecConstantOp(InsnIterator insn) |
| { |
| auto opcode = static_cast<spv::Op>(insn.word(3)); |
| |
| switch (opcode) |
| { |
| case spv::OpIAdd: |
| case spv::OpISub: |
| case spv::OpIMul: |
| case spv::OpUDiv: |
| case spv::OpSDiv: |
| case spv::OpUMod: |
| case spv::OpSMod: |
| case spv::OpSRem: |
| case spv::OpShiftRightLogical: |
| case spv::OpShiftRightArithmetic: |
| case spv::OpShiftLeftLogical: |
| case spv::OpBitwiseOr: |
| case spv::OpLogicalOr: |
| case spv::OpBitwiseAnd: |
| case spv::OpLogicalAnd: |
| case spv::OpBitwiseXor: |
| case spv::OpLogicalEqual: |
| case spv::OpIEqual: |
| case spv::OpLogicalNotEqual: |
| case spv::OpINotEqual: |
| case spv::OpULessThan: |
| case spv::OpSLessThan: |
| case spv::OpUGreaterThan: |
| case spv::OpSGreaterThan: |
| case spv::OpULessThanEqual: |
| case spv::OpSLessThanEqual: |
| case spv::OpUGreaterThanEqual: |
| case spv::OpSGreaterThanEqual: |
| EvalSpecConstantBinaryOp(insn); |
| break; |
| |
| case spv::OpSConvert: |
| case spv::OpFConvert: |
| case spv::OpUConvert: |
| case spv::OpSNegate: |
| case spv::OpNot: |
| case spv::OpLogicalNot: |
| case spv::OpQuantizeToF16: |
| EvalSpecConstantUnaryOp(insn); |
| break; |
| |
| case spv::OpSelect: |
| { |
| auto &result = CreateConstant(insn); |
| auto const &cond = getObject(insn.word(4)); |
| auto condIsScalar = (getType(cond.type).sizeInComponents == 1); |
| auto const &left = getObject(insn.word(5)); |
| auto const &right = getObject(insn.word(6)); |
| |
| for (auto i = 0u; i < getType(result.type).sizeInComponents; i++) |
| { |
| auto sel = cond.constantValue[condIsScalar ? 0 : i]; |
| result.constantValue[i] = sel ? left.constantValue[i] : right.constantValue[i]; |
| } |
| break; |
| } |
| |
| case spv::OpCompositeExtract: |
| { |
| auto &result = CreateConstant(insn); |
| auto const &compositeObject = getObject(insn.word(4)); |
| auto firstComponent = WalkLiteralAccessChain(compositeObject.type, insn.wordCount() - 5, insn.wordPointer(5)); |
| |
| for (auto i = 0u; i < getType(result.type).sizeInComponents; i++) |
| { |
| result.constantValue[i] = compositeObject.constantValue[firstComponent + i]; |
| } |
| break; |
| } |
| |
| case spv::OpCompositeInsert: |
| { |
| auto &result = CreateConstant(insn); |
| auto const &newPart = getObject(insn.word(4)); |
| auto const &oldObject = getObject(insn.word(5)); |
| auto firstNewComponent = WalkLiteralAccessChain(result.type, insn.wordCount() - 6, insn.wordPointer(6)); |
| |
| // old components before |
| for (auto i = 0u; i < firstNewComponent; i++) |
| { |
| result.constantValue[i] = oldObject.constantValue[i]; |
| } |
| // new part |
| for (auto i = 0u; i < getType(newPart.type).sizeInComponents; i++) |
| { |
| result.constantValue[firstNewComponent + i] = newPart.constantValue[i]; |
| } |
| // old components after |
| for (auto i = firstNewComponent + getType(newPart.type).sizeInComponents; i < getType(result.type).sizeInComponents; i++) |
| { |
| result.constantValue[i] = oldObject.constantValue[i]; |
| } |
| break; |
| } |
| |
| case spv::OpVectorShuffle: |
| { |
| auto &result = CreateConstant(insn); |
| auto const &firstHalf = getObject(insn.word(4)); |
| auto const &secondHalf = getObject(insn.word(5)); |
| |
| for (auto i = 0u; i < getType(result.type).sizeInComponents; i++) |
| { |
| auto selector = insn.word(6 + i); |
| if (selector == static_cast<uint32_t>(-1)) |
| { |
| // Undefined value, we'll use zero |
| result.constantValue[i] = 0; |
| } |
| else if (selector < getType(firstHalf.type).sizeInComponents) |
| { |
| result.constantValue[i] = firstHalf.constantValue[selector]; |
| } |
| else |
| { |
| result.constantValue[i] = secondHalf.constantValue[selector - getType(firstHalf.type).sizeInComponents]; |
| } |
| } |
| break; |
| } |
| |
| default: |
| // Other spec constant ops are possible, but require capabilities that are |
| // not exposed in our Vulkan implementation (eg Kernel), so we should never |
| // get here for correct shaders. |
| UNSUPPORTED("EvalSpecConstantOp op: %s", OpcodeName(opcode).c_str()); |
| } |
| } |
| |
| void SpirvShader::EvalSpecConstantUnaryOp(InsnIterator insn) |
| { |
| auto &result = CreateConstant(insn); |
| |
| auto opcode = static_cast<spv::Op>(insn.word(3)); |
| auto const &lhs = getObject(insn.word(4)); |
| auto size = getType(lhs.type).sizeInComponents; |
| |
| for (auto i = 0u; i < size; i++) |
| { |
| auto &v = result.constantValue[i]; |
| auto l = lhs.constantValue[i]; |
| |
| switch (opcode) |
| { |
| case spv::OpSConvert: |
| case spv::OpFConvert: |
| case spv::OpUConvert: |
| UNREACHABLE("Not possible until we have multiple bit widths"); |
| break; |
| |
| case spv::OpSNegate: |
| v = -(int)l; |
| break; |
| case spv::OpNot: |
| case spv::OpLogicalNot: |
| v = ~l; |
| break; |
| |
| case spv::OpQuantizeToF16: |
| { |
| // Can do this nicer with host code, but want to perfectly mirror the reactor code we emit. |
| auto abs = bit_cast<float>(l & 0x7FFFFFFF); |
| auto sign = l & 0x80000000; |
| auto isZero = abs < 0.000061035f ? ~0u : 0u; |
| auto isInf = abs > 65504.0f ? ~0u : 0u; |
| auto isNaN = (abs != abs) ? ~0u : 0u; |
| auto isInfOrNan = isInf | isNaN; |
| v = l & 0xFFFFE000; |
| v &= ~isZero | 0x80000000; |
| v = sign | (isInfOrNan & 0x7F800000) | (~isInfOrNan & v); |
| v |= isNaN & 0x400000; |
| break; |
| } |
| default: |
| UNREACHABLE("EvalSpecConstantUnaryOp op: %s", OpcodeName(opcode).c_str()); |
| } |
| } |
| } |
| |
| void SpirvShader::EvalSpecConstantBinaryOp(InsnIterator insn) |
| { |
| auto &result = CreateConstant(insn); |
| |
| auto opcode = static_cast<spv::Op>(insn.word(3)); |
| auto const &lhs = getObject(insn.word(4)); |
| auto const &rhs = getObject(insn.word(5)); |
| auto size = getType(lhs.type).sizeInComponents; |
| |
| for (auto i = 0u; i < size; i++) |
| { |
| auto &v = result.constantValue[i]; |
| auto l = lhs.constantValue[i]; |
| auto r = rhs.constantValue[i]; |
| |
| switch (opcode) |
| { |
| case spv::OpIAdd: |
| v = l + r; |
| break; |
| case spv::OpISub: |
| v = l - r; |
| break; |
| case spv::OpIMul: |
| v = l * r; |
| break; |
| case spv::OpUDiv: |
| v = (r == 0) ? 0 : l / r; |
| break; |
| case spv::OpUMod: |
| v = (r == 0) ? 0 : l % r; |
| break; |
| case spv::OpSDiv: |
| if (r == 0) r = UINT32_MAX; |
| if (l == static_cast<uint32_t>(INT32_MIN)) l = UINT32_MAX; |
| v = static_cast<int32_t>(l) / static_cast<int32_t>(r); |
| break; |
| case spv::OpSRem: |
| if (r == 0) r = UINT32_MAX; |
| if (l == static_cast<uint32_t>(INT32_MIN)) l = UINT32_MAX; |
| v = static_cast<int32_t>(l) % static_cast<int32_t>(r); |
| break; |
| case spv::OpSMod: |
| if (r == 0) r = UINT32_MAX; |
| if (l == static_cast<uint32_t>(INT32_MIN)) l = UINT32_MAX; |
| // Test if a signed-multiply would be negative. |
| v = static_cast<int32_t>(l) % static_cast<int32_t>(r); |
| if ((v & 0x80000000) != (r & 0x80000000)) |
| v += r; |
| break; |
| case spv::OpShiftRightLogical: |
| v = l >> r; |
| break; |
| case spv::OpShiftRightArithmetic: |
| v = static_cast<int32_t>(l) >> r; |
| break; |
| case spv::OpShiftLeftLogical: |
| v = l << r; |
| break; |
| case spv::OpBitwiseOr: |
| case spv::OpLogicalOr: |
| v = l | r; |
| break; |
| case spv::OpBitwiseAnd: |
| case spv::OpLogicalAnd: |
| v = l & r; |
| break; |
| case spv::OpBitwiseXor: |
| v = l ^ r; |
| break; |
| case spv::OpLogicalEqual: |
| case spv::OpIEqual: |
| v = (l == r) ? ~0u : 0u; |
| break; |
| case spv::OpLogicalNotEqual: |
| case spv::OpINotEqual: |
| v = (l != r) ? ~0u : 0u; |
| break; |
| case spv::OpULessThan: |
| v = l < r ? ~0u : 0u; |
| break; |
| case spv::OpSLessThan: |
| v = static_cast<int32_t>(l) < static_cast<int32_t>(r) ? ~0u : 0u; |
| break; |
| case spv::OpUGreaterThan: |
| v = l > r ? ~0u : 0u; |
| break; |
| case spv::OpSGreaterThan: |
| v = static_cast<int32_t>(l) > static_cast<int32_t>(r) ? ~0u : 0u; |
| break; |
| case spv::OpULessThanEqual: |
| v = l <= r ? ~0u : 0u; |
| break; |
| case spv::OpSLessThanEqual: |
| v = static_cast<int32_t>(l) <= static_cast<int32_t>(r) ? ~0u : 0u; |
| break; |
| case spv::OpUGreaterThanEqual: |
| v = l >= r ? ~0u : 0u; |
| break; |
| case spv::OpSGreaterThanEqual: |
| v = static_cast<int32_t>(l) >= static_cast<int32_t>(r) ? ~0u : 0u; |
| break; |
| default: |
| UNREACHABLE("EvalSpecConstantBinaryOp op: %s", OpcodeName(opcode).c_str()); |
| } |
| } |
| } |
| |
| } // namespace sw |