|  | // 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).componentCount == 1); | 
|  | auto const &left = getObject(insn.word(5)); | 
|  | auto const &right = getObject(insn.word(6)); | 
|  |  | 
|  | for(auto i = 0u; i < getType(result).componentCount; 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.typeId(), insn.wordCount() - 5, insn.wordPointer(5)); | 
|  |  | 
|  | for(auto i = 0u; i < getType(result).componentCount; 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.typeId(), 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).componentCount; i++) | 
|  | { | 
|  | result.constantValue[firstNewComponent + i] = newPart.constantValue[i]; | 
|  | } | 
|  | // old components after | 
|  | for(auto i = firstNewComponent + getType(newPart).componentCount; i < getType(result).componentCount; 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).componentCount; 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).componentCount) | 
|  | { | 
|  | result.constantValue[i] = firstHalf.constantValue[selector]; | 
|  | } | 
|  | else | 
|  | { | 
|  | result.constantValue[i] = secondHalf.constantValue[selector - getType(firstHalf).componentCount]; | 
|  | } | 
|  | } | 
|  | 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).componentCount; | 
|  |  | 
|  | 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).componentCount; | 
|  |  | 
|  | 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 |