Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 1 | // Copyright 2019 The SwiftShader Authors. All Rights Reserved. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include "SpirvShader.hpp" |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 16 | #include "SpirvShaderDebug.hpp" |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 17 | |
| 18 | #include "ShaderCore.hpp" |
| 19 | |
| 20 | #include <spirv/unified1/spirv.hpp> |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 21 | |
| 22 | namespace sw { |
| 23 | |
| 24 | SpirvShader::EmitResult SpirvShader::EmitVectorTimesScalar(InsnIterator insn, EmitState *state) const |
| 25 | { |
Nicolas Capens | 7118675 | 2020-04-09 01:05:31 -0400 | [diff] [blame] | 26 | auto &type = getType(insn.resultTypeId()); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 27 | auto &dst = state->createIntermediate(insn.resultId(), type.componentCount); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 28 | auto lhs = Operand(this, state, insn.word(3)); |
| 29 | auto rhs = Operand(this, state, insn.word(4)); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 30 | |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 31 | for(auto i = 0u; i < type.componentCount; i++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 32 | { |
| 33 | dst.move(i, lhs.Float(i) * rhs.Float(0)); |
| 34 | } |
| 35 | |
| 36 | return EmitResult::Continue; |
| 37 | } |
| 38 | |
| 39 | SpirvShader::EmitResult SpirvShader::EmitMatrixTimesVector(InsnIterator insn, EmitState *state) const |
| 40 | { |
Nicolas Capens | 7118675 | 2020-04-09 01:05:31 -0400 | [diff] [blame] | 41 | auto &type = getType(insn.resultTypeId()); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 42 | auto &dst = state->createIntermediate(insn.resultId(), type.componentCount); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 43 | auto lhs = Operand(this, state, insn.word(3)); |
| 44 | auto rhs = Operand(this, state, insn.word(4)); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 45 | |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 46 | for(auto i = 0u; i < type.componentCount; i++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 47 | { |
| 48 | SIMD::Float v = lhs.Float(i) * rhs.Float(0); |
Nicolas Capens | 2f4b603 | 2020-04-09 02:01:50 -0400 | [diff] [blame] | 49 | for(auto j = 1u; j < rhs.componentCount; j++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 50 | { |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 51 | v += lhs.Float(i + type.componentCount * j) * rhs.Float(j); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 52 | } |
| 53 | dst.move(i, v); |
| 54 | } |
| 55 | |
| 56 | return EmitResult::Continue; |
| 57 | } |
| 58 | |
| 59 | SpirvShader::EmitResult SpirvShader::EmitVectorTimesMatrix(InsnIterator insn, EmitState *state) const |
| 60 | { |
Nicolas Capens | 7118675 | 2020-04-09 01:05:31 -0400 | [diff] [blame] | 61 | auto &type = getType(insn.resultTypeId()); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 62 | auto &dst = state->createIntermediate(insn.resultId(), type.componentCount); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 63 | auto lhs = Operand(this, state, insn.word(3)); |
| 64 | auto rhs = Operand(this, state, insn.word(4)); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 65 | |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 66 | for(auto i = 0u; i < type.componentCount; i++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 67 | { |
Nicolas Capens | 2f4b603 | 2020-04-09 02:01:50 -0400 | [diff] [blame] | 68 | SIMD::Float v = lhs.Float(0) * rhs.Float(i * lhs.componentCount); |
| 69 | for(auto j = 1u; j < lhs.componentCount; j++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 70 | { |
Nicolas Capens | 2f4b603 | 2020-04-09 02:01:50 -0400 | [diff] [blame] | 71 | v += lhs.Float(j) * rhs.Float(i * lhs.componentCount + j); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 72 | } |
| 73 | dst.move(i, v); |
| 74 | } |
| 75 | |
| 76 | return EmitResult::Continue; |
| 77 | } |
| 78 | |
| 79 | SpirvShader::EmitResult SpirvShader::EmitMatrixTimesMatrix(InsnIterator insn, EmitState *state) const |
| 80 | { |
Nicolas Capens | 7118675 | 2020-04-09 01:05:31 -0400 | [diff] [blame] | 81 | auto &type = getType(insn.resultTypeId()); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 82 | auto &dst = state->createIntermediate(insn.resultId(), type.componentCount); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 83 | auto lhs = Operand(this, state, insn.word(3)); |
| 84 | auto rhs = Operand(this, state, insn.word(4)); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 85 | |
| 86 | auto numColumns = type.definition.word(3); |
| 87 | auto numRows = getType(type.definition.word(2)).definition.word(3); |
Nicolas Capens | 72f089c | 2020-04-08 23:37:08 -0400 | [diff] [blame] | 88 | auto numAdds = getType(getObject(insn.word(3))).definition.word(3); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 89 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 90 | for(auto row = 0u; row < numRows; row++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 91 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 92 | for(auto col = 0u; col < numColumns; col++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 93 | { |
| 94 | SIMD::Float v = SIMD::Float(0); |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 95 | for(auto i = 0u; i < numAdds; i++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 96 | { |
| 97 | v += lhs.Float(i * numRows + row) * rhs.Float(col * numAdds + i); |
| 98 | } |
| 99 | dst.move(numRows * col + row, v); |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | return EmitResult::Continue; |
| 104 | } |
| 105 | |
| 106 | SpirvShader::EmitResult SpirvShader::EmitOuterProduct(InsnIterator insn, EmitState *state) const |
| 107 | { |
Nicolas Capens | 7118675 | 2020-04-09 01:05:31 -0400 | [diff] [blame] | 108 | auto &type = getType(insn.resultTypeId()); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 109 | auto &dst = state->createIntermediate(insn.resultId(), type.componentCount); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 110 | auto lhs = Operand(this, state, insn.word(3)); |
| 111 | auto rhs = Operand(this, state, insn.word(4)); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 112 | |
Nicolas Capens | 2f4b603 | 2020-04-09 02:01:50 -0400 | [diff] [blame] | 113 | auto numRows = lhs.componentCount; |
| 114 | auto numCols = rhs.componentCount; |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 115 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 116 | for(auto col = 0u; col < numCols; col++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 117 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 118 | for(auto row = 0u; row < numRows; row++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 119 | { |
| 120 | dst.move(col * numRows + row, lhs.Float(row) * rhs.Float(col)); |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | return EmitResult::Continue; |
| 125 | } |
| 126 | |
| 127 | SpirvShader::EmitResult SpirvShader::EmitTranspose(InsnIterator insn, EmitState *state) const |
| 128 | { |
Nicolas Capens | 7118675 | 2020-04-09 01:05:31 -0400 | [diff] [blame] | 129 | auto &type = getType(insn.resultTypeId()); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 130 | auto &dst = state->createIntermediate(insn.resultId(), type.componentCount); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 131 | auto mat = Operand(this, state, insn.word(3)); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 132 | |
| 133 | auto numCols = type.definition.word(3); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 134 | auto numRows = getType(type.definition.word(2)).componentCount; |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 135 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 136 | for(auto col = 0u; col < numCols; col++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 137 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 138 | for(auto row = 0u; row < numRows; row++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 139 | { |
| 140 | dst.move(col * numRows + row, mat.Float(row * numCols + col)); |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | return EmitResult::Continue; |
| 145 | } |
| 146 | |
| 147 | SpirvShader::EmitResult SpirvShader::EmitUnaryOp(InsnIterator insn, EmitState *state) const |
| 148 | { |
Nicolas Capens | 7118675 | 2020-04-09 01:05:31 -0400 | [diff] [blame] | 149 | auto &type = getType(insn.resultTypeId()); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 150 | auto &dst = state->createIntermediate(insn.resultId(), type.componentCount); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 151 | auto src = Operand(this, state, insn.word(3)); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 152 | |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 153 | for(auto i = 0u; i < type.componentCount; i++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 154 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 155 | switch(insn.opcode()) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 156 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 157 | case spv::OpNot: |
| 158 | case spv::OpLogicalNot: // logical not == bitwise not due to all-bits boolean representation |
| 159 | dst.move(i, ~src.UInt(i)); |
| 160 | break; |
| 161 | case spv::OpBitFieldInsert: |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 162 | { |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 163 | auto insert = Operand(this, state, insn.word(4)).UInt(i); |
| 164 | auto offset = Operand(this, state, insn.word(5)).UInt(0); |
| 165 | auto count = Operand(this, state, insn.word(6)).UInt(0); |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 166 | auto one = SIMD::UInt(1); |
| 167 | auto v = src.UInt(i); |
| 168 | auto mask = Bitmask32(offset + count) ^ Bitmask32(offset); |
| 169 | dst.move(i, (v & ~mask) | ((insert << offset) & mask)); |
| 170 | break; |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 171 | } |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 172 | case spv::OpBitFieldSExtract: |
| 173 | case spv::OpBitFieldUExtract: |
| 174 | { |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 175 | auto offset = Operand(this, state, insn.word(4)).UInt(0); |
| 176 | auto count = Operand(this, state, insn.word(5)).UInt(0); |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 177 | auto one = SIMD::UInt(1); |
| 178 | auto v = src.UInt(i); |
| 179 | SIMD::UInt out = (v >> offset) & Bitmask32(count); |
| 180 | if(insn.opcode() == spv::OpBitFieldSExtract) |
| 181 | { |
| 182 | auto sign = out & NthBit32(count - one); |
| 183 | auto sext = ~(sign - one); |
| 184 | out |= sext; |
| 185 | } |
| 186 | dst.move(i, out); |
| 187 | break; |
| 188 | } |
| 189 | case spv::OpBitReverse: |
| 190 | { |
| 191 | // TODO: Add an intrinsic to reactor. Even if there isn't a |
| 192 | // single vector instruction, there may be target-dependent |
| 193 | // ways to make this faster. |
| 194 | // https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel |
| 195 | SIMD::UInt v = src.UInt(i); |
| 196 | v = ((v >> 1) & SIMD::UInt(0x55555555)) | ((v & SIMD::UInt(0x55555555)) << 1); |
| 197 | v = ((v >> 2) & SIMD::UInt(0x33333333)) | ((v & SIMD::UInt(0x33333333)) << 2); |
| 198 | v = ((v >> 4) & SIMD::UInt(0x0F0F0F0F)) | ((v & SIMD::UInt(0x0F0F0F0F)) << 4); |
| 199 | v = ((v >> 8) & SIMD::UInt(0x00FF00FF)) | ((v & SIMD::UInt(0x00FF00FF)) << 8); |
| 200 | v = (v >> 16) | (v << 16); |
| 201 | dst.move(i, v); |
| 202 | break; |
| 203 | } |
| 204 | case spv::OpBitCount: |
| 205 | dst.move(i, CountBits(src.UInt(i))); |
| 206 | break; |
| 207 | case spv::OpSNegate: |
| 208 | dst.move(i, -src.Int(i)); |
| 209 | break; |
| 210 | case spv::OpFNegate: |
| 211 | dst.move(i, -src.Float(i)); |
| 212 | break; |
| 213 | case spv::OpConvertFToU: |
| 214 | dst.move(i, SIMD::UInt(src.Float(i))); |
| 215 | break; |
| 216 | case spv::OpConvertFToS: |
| 217 | dst.move(i, SIMD::Int(src.Float(i))); |
| 218 | break; |
| 219 | case spv::OpConvertSToF: |
| 220 | dst.move(i, SIMD::Float(src.Int(i))); |
| 221 | break; |
| 222 | case spv::OpConvertUToF: |
| 223 | dst.move(i, SIMD::Float(src.UInt(i))); |
| 224 | break; |
| 225 | case spv::OpBitcast: |
| 226 | dst.move(i, src.Float(i)); |
| 227 | break; |
| 228 | case spv::OpIsInf: |
| 229 | dst.move(i, IsInf(src.Float(i))); |
| 230 | break; |
| 231 | case spv::OpIsNan: |
| 232 | dst.move(i, IsNan(src.Float(i))); |
| 233 | break; |
| 234 | case spv::OpDPdx: |
| 235 | case spv::OpDPdxCoarse: |
| 236 | // Derivative instructions: FS invocations are laid out like so: |
| 237 | // 0 1 |
| 238 | // 2 3 |
| 239 | static_assert(SIMD::Width == 4, "All cross-lane instructions will need care when using a different width"); |
| 240 | dst.move(i, SIMD::Float(Extract(src.Float(i), 1) - Extract(src.Float(i), 0))); |
| 241 | break; |
| 242 | case spv::OpDPdy: |
| 243 | case spv::OpDPdyCoarse: |
| 244 | dst.move(i, SIMD::Float(Extract(src.Float(i), 2) - Extract(src.Float(i), 0))); |
| 245 | break; |
| 246 | case spv::OpFwidth: |
| 247 | case spv::OpFwidthCoarse: |
| 248 | dst.move(i, SIMD::Float(Abs(Extract(src.Float(i), 1) - Extract(src.Float(i), 0)) + Abs(Extract(src.Float(i), 2) - Extract(src.Float(i), 0)))); |
| 249 | break; |
| 250 | case spv::OpDPdxFine: |
| 251 | { |
| 252 | auto firstRow = Extract(src.Float(i), 1) - Extract(src.Float(i), 0); |
| 253 | auto secondRow = Extract(src.Float(i), 3) - Extract(src.Float(i), 2); |
| 254 | SIMD::Float v = SIMD::Float(firstRow); |
| 255 | v = Insert(v, secondRow, 2); |
| 256 | v = Insert(v, secondRow, 3); |
| 257 | dst.move(i, v); |
| 258 | break; |
| 259 | } |
| 260 | case spv::OpDPdyFine: |
| 261 | { |
| 262 | auto firstColumn = Extract(src.Float(i), 2) - Extract(src.Float(i), 0); |
| 263 | auto secondColumn = Extract(src.Float(i), 3) - Extract(src.Float(i), 1); |
| 264 | SIMD::Float v = SIMD::Float(firstColumn); |
| 265 | v = Insert(v, secondColumn, 1); |
| 266 | v = Insert(v, secondColumn, 3); |
| 267 | dst.move(i, v); |
| 268 | break; |
| 269 | } |
| 270 | case spv::OpFwidthFine: |
| 271 | { |
| 272 | auto firstRow = Extract(src.Float(i), 1) - Extract(src.Float(i), 0); |
| 273 | auto secondRow = Extract(src.Float(i), 3) - Extract(src.Float(i), 2); |
| 274 | SIMD::Float dpdx = SIMD::Float(firstRow); |
| 275 | dpdx = Insert(dpdx, secondRow, 2); |
| 276 | dpdx = Insert(dpdx, secondRow, 3); |
| 277 | auto firstColumn = Extract(src.Float(i), 2) - Extract(src.Float(i), 0); |
| 278 | auto secondColumn = Extract(src.Float(i), 3) - Extract(src.Float(i), 1); |
| 279 | SIMD::Float dpdy = SIMD::Float(firstColumn); |
| 280 | dpdy = Insert(dpdy, secondColumn, 1); |
| 281 | dpdy = Insert(dpdy, secondColumn, 3); |
| 282 | dst.move(i, Abs(dpdx) + Abs(dpdy)); |
| 283 | break; |
| 284 | } |
| 285 | case spv::OpQuantizeToF16: |
| 286 | { |
| 287 | // Note: keep in sync with the specialization constant version in EvalSpecConstantUnaryOp |
| 288 | auto abs = Abs(src.Float(i)); |
| 289 | auto sign = src.Int(i) & SIMD::Int(0x80000000); |
| 290 | auto isZero = CmpLT(abs, SIMD::Float(0.000061035f)); |
| 291 | auto isInf = CmpGT(abs, SIMD::Float(65504.0f)); |
| 292 | auto isNaN = IsNan(abs); |
| 293 | auto isInfOrNan = isInf | isNaN; |
| 294 | SIMD::Int v = src.Int(i) & SIMD::Int(0xFFFFE000); |
| 295 | v &= ~isZero | SIMD::Int(0x80000000); |
| 296 | v = sign | (isInfOrNan & SIMD::Int(0x7F800000)) | (~isInfOrNan & v); |
| 297 | v |= isNaN & SIMD::Int(0x400000); |
| 298 | dst.move(i, v); |
| 299 | break; |
| 300 | } |
| 301 | default: |
| 302 | UNREACHABLE("%s", OpcodeName(insn.opcode()).c_str()); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 303 | } |
| 304 | } |
| 305 | |
| 306 | return EmitResult::Continue; |
| 307 | } |
| 308 | |
| 309 | SpirvShader::EmitResult SpirvShader::EmitBinaryOp(InsnIterator insn, EmitState *state) const |
| 310 | { |
Nicolas Capens | 7118675 | 2020-04-09 01:05:31 -0400 | [diff] [blame] | 311 | auto &type = getType(insn.resultTypeId()); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 312 | auto &dst = state->createIntermediate(insn.resultId(), type.componentCount); |
Nicolas Capens | 72f089c | 2020-04-08 23:37:08 -0400 | [diff] [blame] | 313 | auto &lhsType = getType(getObject(insn.word(3))); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 314 | auto lhs = Operand(this, state, insn.word(3)); |
| 315 | auto rhs = Operand(this, state, insn.word(4)); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 316 | |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 317 | for(auto i = 0u; i < lhsType.componentCount; i++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 318 | { |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 319 | switch(insn.opcode()) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 320 | { |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 321 | case spv::OpIAdd: |
| 322 | dst.move(i, lhs.Int(i) + rhs.Int(i)); |
| 323 | break; |
| 324 | case spv::OpISub: |
| 325 | dst.move(i, lhs.Int(i) - rhs.Int(i)); |
| 326 | break; |
| 327 | case spv::OpIMul: |
| 328 | dst.move(i, lhs.Int(i) * rhs.Int(i)); |
| 329 | break; |
| 330 | case spv::OpSDiv: |
| 331 | { |
| 332 | SIMD::Int a = lhs.Int(i); |
| 333 | SIMD::Int b = rhs.Int(i); |
| 334 | b = b | CmpEQ(b, SIMD::Int(0)); // prevent divide-by-zero |
| 335 | a = a | (CmpEQ(a, SIMD::Int(0x80000000)) & CmpEQ(b, SIMD::Int(-1))); // prevent integer overflow |
| 336 | dst.move(i, a / b); |
| 337 | break; |
| 338 | } |
| 339 | case spv::OpUDiv: |
| 340 | { |
| 341 | auto zeroMask = As<SIMD::UInt>(CmpEQ(rhs.Int(i), SIMD::Int(0))); |
| 342 | dst.move(i, lhs.UInt(i) / (rhs.UInt(i) | zeroMask)); |
| 343 | break; |
| 344 | } |
| 345 | case spv::OpSRem: |
| 346 | { |
| 347 | SIMD::Int a = lhs.Int(i); |
| 348 | SIMD::Int b = rhs.Int(i); |
| 349 | b = b | CmpEQ(b, SIMD::Int(0)); // prevent divide-by-zero |
| 350 | a = a | (CmpEQ(a, SIMD::Int(0x80000000)) & CmpEQ(b, SIMD::Int(-1))); // prevent integer overflow |
| 351 | dst.move(i, a % b); |
| 352 | break; |
| 353 | } |
| 354 | case spv::OpSMod: |
| 355 | { |
| 356 | SIMD::Int a = lhs.Int(i); |
| 357 | SIMD::Int b = rhs.Int(i); |
| 358 | b = b | CmpEQ(b, SIMD::Int(0)); // prevent divide-by-zero |
| 359 | a = a | (CmpEQ(a, SIMD::Int(0x80000000)) & CmpEQ(b, SIMD::Int(-1))); // prevent integer overflow |
| 360 | auto mod = a % b; |
| 361 | // If a and b have opposite signs, the remainder operation takes |
| 362 | // the sign from a but OpSMod is supposed to take the sign of b. |
| 363 | // Adding b will ensure that the result has the correct sign and |
| 364 | // that it is still congruent to a modulo b. |
| 365 | // |
| 366 | // See also http://mathforum.org/library/drmath/view/52343.html |
| 367 | auto signDiff = CmpNEQ(CmpGE(a, SIMD::Int(0)), CmpGE(b, SIMD::Int(0))); |
| 368 | auto fixedMod = mod + (b & CmpNEQ(mod, SIMD::Int(0)) & signDiff); |
| 369 | dst.move(i, As<SIMD::Float>(fixedMod)); |
| 370 | break; |
| 371 | } |
| 372 | case spv::OpUMod: |
| 373 | { |
| 374 | auto zeroMask = As<SIMD::UInt>(CmpEQ(rhs.Int(i), SIMD::Int(0))); |
| 375 | dst.move(i, lhs.UInt(i) % (rhs.UInt(i) | zeroMask)); |
| 376 | break; |
| 377 | } |
| 378 | case spv::OpIEqual: |
| 379 | case spv::OpLogicalEqual: |
| 380 | dst.move(i, CmpEQ(lhs.Int(i), rhs.Int(i))); |
| 381 | break; |
| 382 | case spv::OpINotEqual: |
| 383 | case spv::OpLogicalNotEqual: |
| 384 | dst.move(i, CmpNEQ(lhs.Int(i), rhs.Int(i))); |
| 385 | break; |
| 386 | case spv::OpUGreaterThan: |
| 387 | dst.move(i, CmpGT(lhs.UInt(i), rhs.UInt(i))); |
| 388 | break; |
| 389 | case spv::OpSGreaterThan: |
| 390 | dst.move(i, CmpGT(lhs.Int(i), rhs.Int(i))); |
| 391 | break; |
| 392 | case spv::OpUGreaterThanEqual: |
| 393 | dst.move(i, CmpGE(lhs.UInt(i), rhs.UInt(i))); |
| 394 | break; |
| 395 | case spv::OpSGreaterThanEqual: |
| 396 | dst.move(i, CmpGE(lhs.Int(i), rhs.Int(i))); |
| 397 | break; |
| 398 | case spv::OpULessThan: |
| 399 | dst.move(i, CmpLT(lhs.UInt(i), rhs.UInt(i))); |
| 400 | break; |
| 401 | case spv::OpSLessThan: |
| 402 | dst.move(i, CmpLT(lhs.Int(i), rhs.Int(i))); |
| 403 | break; |
| 404 | case spv::OpULessThanEqual: |
| 405 | dst.move(i, CmpLE(lhs.UInt(i), rhs.UInt(i))); |
| 406 | break; |
| 407 | case spv::OpSLessThanEqual: |
| 408 | dst.move(i, CmpLE(lhs.Int(i), rhs.Int(i))); |
| 409 | break; |
| 410 | case spv::OpFAdd: |
| 411 | dst.move(i, lhs.Float(i) + rhs.Float(i)); |
| 412 | break; |
| 413 | case spv::OpFSub: |
| 414 | dst.move(i, lhs.Float(i) - rhs.Float(i)); |
| 415 | break; |
| 416 | case spv::OpFMul: |
| 417 | dst.move(i, lhs.Float(i) * rhs.Float(i)); |
| 418 | break; |
| 419 | case spv::OpFDiv: |
| 420 | dst.move(i, lhs.Float(i) / rhs.Float(i)); |
| 421 | break; |
| 422 | case spv::OpFMod: |
| 423 | // TODO(b/126873455): inaccurate for values greater than 2^24 |
| 424 | dst.move(i, lhs.Float(i) - rhs.Float(i) * Floor(lhs.Float(i) / rhs.Float(i))); |
| 425 | break; |
| 426 | case spv::OpFRem: |
| 427 | dst.move(i, lhs.Float(i) % rhs.Float(i)); |
| 428 | break; |
| 429 | case spv::OpFOrdEqual: |
| 430 | dst.move(i, CmpEQ(lhs.Float(i), rhs.Float(i))); |
| 431 | break; |
| 432 | case spv::OpFUnordEqual: |
| 433 | dst.move(i, CmpUEQ(lhs.Float(i), rhs.Float(i))); |
| 434 | break; |
| 435 | case spv::OpFOrdNotEqual: |
| 436 | dst.move(i, CmpNEQ(lhs.Float(i), rhs.Float(i))); |
| 437 | break; |
| 438 | case spv::OpFUnordNotEqual: |
| 439 | dst.move(i, CmpUNEQ(lhs.Float(i), rhs.Float(i))); |
| 440 | break; |
| 441 | case spv::OpFOrdLessThan: |
| 442 | dst.move(i, CmpLT(lhs.Float(i), rhs.Float(i))); |
| 443 | break; |
| 444 | case spv::OpFUnordLessThan: |
| 445 | dst.move(i, CmpULT(lhs.Float(i), rhs.Float(i))); |
| 446 | break; |
| 447 | case spv::OpFOrdGreaterThan: |
| 448 | dst.move(i, CmpGT(lhs.Float(i), rhs.Float(i))); |
| 449 | break; |
| 450 | case spv::OpFUnordGreaterThan: |
| 451 | dst.move(i, CmpUGT(lhs.Float(i), rhs.Float(i))); |
| 452 | break; |
| 453 | case spv::OpFOrdLessThanEqual: |
| 454 | dst.move(i, CmpLE(lhs.Float(i), rhs.Float(i))); |
| 455 | break; |
| 456 | case spv::OpFUnordLessThanEqual: |
| 457 | dst.move(i, CmpULE(lhs.Float(i), rhs.Float(i))); |
| 458 | break; |
| 459 | case spv::OpFOrdGreaterThanEqual: |
| 460 | dst.move(i, CmpGE(lhs.Float(i), rhs.Float(i))); |
| 461 | break; |
| 462 | case spv::OpFUnordGreaterThanEqual: |
| 463 | dst.move(i, CmpUGE(lhs.Float(i), rhs.Float(i))); |
| 464 | break; |
| 465 | case spv::OpShiftRightLogical: |
| 466 | dst.move(i, lhs.UInt(i) >> rhs.UInt(i)); |
| 467 | break; |
| 468 | case spv::OpShiftRightArithmetic: |
| 469 | dst.move(i, lhs.Int(i) >> rhs.Int(i)); |
| 470 | break; |
| 471 | case spv::OpShiftLeftLogical: |
| 472 | dst.move(i, lhs.UInt(i) << rhs.UInt(i)); |
| 473 | break; |
| 474 | case spv::OpBitwiseOr: |
| 475 | case spv::OpLogicalOr: |
| 476 | dst.move(i, lhs.UInt(i) | rhs.UInt(i)); |
| 477 | break; |
| 478 | case spv::OpBitwiseXor: |
| 479 | dst.move(i, lhs.UInt(i) ^ rhs.UInt(i)); |
| 480 | break; |
| 481 | case spv::OpBitwiseAnd: |
| 482 | case spv::OpLogicalAnd: |
| 483 | dst.move(i, lhs.UInt(i) & rhs.UInt(i)); |
| 484 | break; |
| 485 | case spv::OpSMulExtended: |
| 486 | // Extended ops: result is a structure containing two members of the same type as lhs & rhs. |
| 487 | // In our flat view then, component i is the i'th component of the first member; |
| 488 | // component i + N is the i'th component of the second member. |
| 489 | dst.move(i, lhs.Int(i) * rhs.Int(i)); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 490 | dst.move(i + lhsType.componentCount, MulHigh(lhs.Int(i), rhs.Int(i))); |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 491 | break; |
| 492 | case spv::OpUMulExtended: |
| 493 | dst.move(i, lhs.UInt(i) * rhs.UInt(i)); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 494 | dst.move(i + lhsType.componentCount, MulHigh(lhs.UInt(i), rhs.UInt(i))); |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 495 | break; |
| 496 | case spv::OpIAddCarry: |
| 497 | dst.move(i, lhs.UInt(i) + rhs.UInt(i)); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 498 | dst.move(i + lhsType.componentCount, CmpLT(dst.UInt(i), lhs.UInt(i)) >> 31); |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 499 | break; |
| 500 | case spv::OpISubBorrow: |
| 501 | dst.move(i, lhs.UInt(i) - rhs.UInt(i)); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 502 | dst.move(i + lhsType.componentCount, CmpLT(lhs.UInt(i), rhs.UInt(i)) >> 31); |
Ben Clayton | bc1c067 | 2019-12-17 20:37:37 +0000 | [diff] [blame] | 503 | break; |
| 504 | default: |
| 505 | UNREACHABLE("%s", OpcodeName(insn.opcode()).c_str()); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 506 | } |
| 507 | } |
| 508 | |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 509 | SPIRV_SHADER_DBG("{0}: {1}", insn.word(2), dst); |
| 510 | SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), lhs); |
| 511 | SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), rhs); |
| 512 | |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 513 | return EmitResult::Continue; |
| 514 | } |
| 515 | |
| 516 | SpirvShader::EmitResult SpirvShader::EmitDot(InsnIterator insn, EmitState *state) const |
| 517 | { |
Nicolas Capens | 7118675 | 2020-04-09 01:05:31 -0400 | [diff] [blame] | 518 | auto &type = getType(insn.resultTypeId()); |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 519 | ASSERT(type.componentCount == 1); |
| 520 | auto &dst = state->createIntermediate(insn.resultId(), type.componentCount); |
Nicolas Capens | 72f089c | 2020-04-08 23:37:08 -0400 | [diff] [blame] | 521 | auto &lhsType = getType(getObject(insn.word(3))); |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 522 | auto lhs = Operand(this, state, insn.word(3)); |
| 523 | auto rhs = Operand(this, state, insn.word(4)); |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 524 | |
Nicolas Capens | ff9f9b5 | 2020-04-14 00:46:38 -0400 | [diff] [blame] | 525 | dst.move(0, Dot(lhsType.componentCount, lhs, rhs)); |
Ben Clayton | fc951cd | 2019-05-15 17:16:56 +0100 | [diff] [blame] | 526 | |
| 527 | SPIRV_SHADER_DBG("{0}: {1}", insn.resultId(), dst); |
| 528 | SPIRV_SHADER_DBG("{0}: {1}", insn.word(3), lhs); |
| 529 | SPIRV_SHADER_DBG("{0}: {1}", insn.word(4), rhs); |
| 530 | |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 531 | return EmitResult::Continue; |
| 532 | } |
| 533 | |
Nicolas Capens | e6f65d9 | 2020-04-08 21:55:43 -0400 | [diff] [blame] | 534 | SIMD::Float SpirvShader::Dot(unsigned numComponents, Operand const &x, Operand const &y) const |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 535 | { |
| 536 | SIMD::Float d = x.Float(0) * y.Float(0); |
| 537 | |
Nicolas Capens | 81bc9d9 | 2019-12-16 15:05:57 -0500 | [diff] [blame] | 538 | for(auto i = 1u; i < numComponents; i++) |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 539 | { |
| 540 | d += x.Float(i) * y.Float(i); |
| 541 | } |
| 542 | |
| 543 | return d; |
| 544 | } |
| 545 | |
Ben Clayton | 474dba4 | 2019-11-28 13:43:55 +0000 | [diff] [blame] | 546 | std::pair<SIMD::Float, SIMD::Int> SpirvShader::Frexp(RValue<SIMD::Float> val) const |
| 547 | { |
| 548 | // Assumes IEEE 754 |
| 549 | auto v = As<SIMD::UInt>(val); |
| 550 | auto isNotZero = CmpNEQ(v & SIMD::UInt(0x7FFFFFFF), SIMD::UInt(0)); |
| 551 | auto zeroSign = v & SIMD::UInt(0x80000000) & ~isNotZero; |
| 552 | auto significand = As<SIMD::Float>((((v & SIMD::UInt(0x807FFFFF)) | SIMD::UInt(0x3F000000)) & isNotZero) | zeroSign); |
| 553 | auto exponent = Exponent(val) & SIMD::Int(isNotZero); |
| 554 | return std::make_pair(significand, exponent); |
| 555 | } |
| 556 | |
| 557 | } // namespace sw |