{"record":{"id":"d14d3254fc97e100","repo":"stride3d/stride","slug":"unsupported-binary-op-op","errorCode":null,"errorMessage":"Unsupported binary op: {op}","messagePattern":"Unsupported binary op: (.+?)","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs","lineNumber":168,"sourceCode":"                case Specification.Op.OpFMul:\n                case Specification.Op.OpFDiv:\n                    if (!TryGetConstantValue(i.Data.Memory.Span[4], out var left, out var leftTypeId))\n                        return false;\n                    if (!TryGetConstantValue(i.Data.Memory.Span[5], out var right, out var rightTypeId))\n                        return false;\n                    if (leftTypeId != resultType || rightTypeId != resultType)\n                        return false;\n                    value = op switch\n                    {\n                        // Note: first cast to object is important, otherwise int/float will be cast as float\n                        Specification.Op.OpIAdd => (object)((int)left + (int)right),\n                        Specification.Op.OpISub => (int)left - (int)right,\n                        Specification.Op.OpIMul => (int)left * (int)right,\n                        Specification.Op.OpFAdd => (float)left + (float)right,\n                        Specification.Op.OpFSub => (float)left - (float)right,\n                        Specification.Op.OpFMul => (float)left * (float)right,\n                        Specification.Op.OpFDiv => (float)left / (float)right,\n                        _ => throw new NotSupportedException($\"Unsupported binary op: {op}\"),\n                    };\n                    break;\n                default:\n                    throw new NotImplementedException();\n            }\n\n            return true;\n        }\n\n        if ((i.Op == Specification.Op.OpConstantComposite || i.Op == Specification.Op.OpSpecConstantComposite) &&\n            (OpConstantComposite)i is { } constantComposite)\n        {\n            var values = constantComposite.Constituents;\n            var constants = new object[values.WordCount];\n            for (int j = 0; j < values.WordCount; ++j)\n            {\n                if (!TryGetConstantValue(values.Elements.Span[j], out constants[j]!, out _))\n                    return false;","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs#L150-L186","documentation":"Binary constant folding implements only OpIAdd, OpISub, OpIMul, OpFAdd, OpFSub, OpFMul, and OpFDiv (all as int or float); any other binary opcode reaching the default arm throws NotSupportedException 'Unsupported binary op'. Notably absent are OpUDiv, OpSRem/OpSMod, OpShift*/OpBitwise*, and OpFRem/OpFMod.","triggerScenarios":"Constant-folding OpUDiv, OpSRem, OpSMod, OpShiftLeftLogical, OpBitwiseAnd/Or/Xor, OpFRem/OpFMod, or any other binary opcode with constant operands.","commonSituations":"Constant expressions with integer division/remainder (e.g. array index math like i % 4), bitmask composition of constant flags, or shift-based packing in generated shaders.","solutions":["Pre-compute the binary result and emit the folded constant directly.","Extend the switch with the needed arms (e.g. OpUDiv => (int)left / (int)right guarded for zero, OpBitwiseAnd => (int)left & (int)right, OpShiftLeftLogical => (int)left << (int)right).","Emit the operation as a runtime instruction instead of relying on fold-time evaluation."],"exampleFix":"// before\nSpecification.Op.OpFDiv => (float)left / (float)right,\n_ => throw new NotSupportedException(...)\n// after\nSpecification.Op.OpFDiv => (float)left / (float)right,\nSpecification.Op.OpSRem => (int)left % (int)right,\nSpecification.Op.OpBitwiseAnd => (int)left & (int)right,\n_ => throw new NotSupportedException(...)","handlingStrategy":"validation","validationCode":"static readonly Specification.Op[] FoldableBinary = { Specification.Op.OpIAdd, Specification.Op.OpISub, Specification.Op.OpIMul, Specification.Op.OpFAdd, Specification.Op.OpFSub, Specification.Op.OpFMul, Specification.Op.OpFDiv };\nif (!FoldableBinary.Contains(op)) EmitRuntimeInstruction(op);","typeGuard":"bool IsFoldableBinary(Specification.Op op) => op is Specification.Op.OpIAdd or Specification.Op.OpISub or Specification.Op.OpIMul or Specification.Op.OpFAdd or Specification.Op.OpFSub or Specification.Op.OpFMul or Specification.Op.OpFDiv;","tryCatchPattern":"try { folded = context.GetConstantValue(binaryId); } catch (NotSupportedException ex) when (ex.Message.StartsWith(\"Unsupported binary op\")) { EmitRuntimeInstruction(op); }","preventionTips":["Fold only add/sub/mul/div in constant expressions; emit %, shifts, bitwise ops at runtime","Guard OpFDiv/OpUDiv constants against zero divisors","Keep a registry of foldable opcodes synchronized with TryGetConstantValue's switch"],"tags":["spirv","constant-folding","binary","not-supported"],"backgroundTag":"unsupported-operation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}