// 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 namespace sw { void SpirvShader::EvalSpecConstantOp(InsnIterator insn) { auto opcode = static_cast(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(-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)); } } void SpirvShader::EvalSpecConstantUnaryOp(InsnIterator insn) { auto &result = CreateConstant(insn); auto opcode = static_cast(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(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)); } } } void SpirvShader::EvalSpecConstantBinaryOp(InsnIterator insn) { auto &result = CreateConstant(insn); auto opcode = static_cast(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(INT32_MIN)) l = UINT32_MAX; v = static_cast(l) / static_cast(r); break; case spv::OpSRem: if(r == 0) r = UINT32_MAX; if(l == static_cast(INT32_MIN)) l = UINT32_MAX; v = static_cast(l) % static_cast(r); break; case spv::OpSMod: if(r == 0) r = UINT32_MAX; if(l == static_cast(INT32_MIN)) l = UINT32_MAX; // Test if a signed-multiply would be negative. v = static_cast(l) % static_cast(r); if((v & 0x80000000) != (r & 0x80000000)) v += r; break; case spv::OpShiftRightLogical: v = l >> r; break; case spv::OpShiftRightArithmetic: v = static_cast(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(l) < static_cast(r) ? ~0u : 0u; break; case spv::OpUGreaterThan: v = l > r ? ~0u : 0u; break; case spv::OpSGreaterThan: v = static_cast(l) > static_cast(r) ? ~0u : 0u; break; case spv::OpULessThanEqual: v = l <= r ? ~0u : 0u; break; case spv::OpSLessThanEqual: v = static_cast(l) <= static_cast(r) ? ~0u : 0u; break; case spv::OpUGreaterThanEqual: v = l >= r ? ~0u : 0u; break; case spv::OpSGreaterThanEqual: v = static_cast(l) >= static_cast(r) ? ~0u : 0u; break; default: UNREACHABLE("EvalSpecConstantBinaryOp op: %s", OpcodeName(opcode)); } } } } // namespace sw