function SPIRV()

in benchmarks/JetStream2/WSL/SPIR-V.js [29:249]


function SPIRV(json) {
    let result = {
        ops: {},
        kinds: {}
    }

    let composites = new Map();
    let ids = new Map();
    for (let kind of json.operand_kinds) {
        switch (kind.category) {
        case "BitEnum":
        case "ValueEnum":
            let enumerants = { category: kind.category };
            for (let enumerant of kind.enumerants) {
                enumerants[enumerant.enumerant] = enumerant;
            }
            result.kinds[kind.kind] = enumerants;
            break;
        case "Composite":
            composites.set(kind.kind, kind);
            break;
        case "Id":
            ids.set(kind.kind, kind);
            break;
        }
    }

    function matchType(operandInfoKind, operand) {
        switch (operandInfoKind) {
            // FIXME: I'm not actually sure that Ids should be unsigned.
            case "IdResultType":
            case "IdResult":
            case "IdRef":
            case "IdScope":
            case "IdMemorySemantics":
            case "LiteralExtInstInteger":
                if (typeof operand != "number")
                    throw new Error("Operand needs to be a number");
                if ((operand >>> 0) != operand)
                    throw new Error("Operand needs to fit in an unsigned int");
                return;
            case "LiteralInteger":
                if (typeof operand != "number")
                    throw new Error("Operand needs to be a number");
                if ((operand | 0) != operand)
                    throw new Error("Operand needs to fit in an int");
                return;
            case "LiteralString":
                if (typeof operand != "string")
                    throw new Error("Operand needs to be a string");
                return;
            case "LiteralContextDependentNumber":
            case "LiteralSpecConstantOpInteger":
                if (typeof operand != "number")
                    throw new Error("Operand needs to be a number");
                if ((operand >>> 0) != operand && (operand | 0) != operand)
                    throw new Error("Operand needs to fit in an unsigned int or an int.");
                return;
        }
        let kind = result.kinds[operandInfoKind];
        if (kind) {
            if (operand instanceof Array) {
                if (kind.category != "BitEnum")
                    throw new Error("Passing an array to a " + kind.category + " operand");
                for (let operandItem of operand) {
                    if (kind[operandItem.enumerant] != operandItem)
                        throw new Error("" + operandItem.enumerant + " is not a member of " + operandInfoKind);
                }
                return;
            }
            if (kind[operand.enumerant] != operand)
                throw new Error("" + operand.enumerant + " is not a member of " + operandInfoKind);
            return;
        }
        throw new Error("Unknown type: " + operandInfoKind);
    }

    class OperandChecker {
        constructor(operandInfos)
        {
            this._operandInfos = operandInfos || [];
            this._operandIndex = 0;
            this._operandInfoIndex = 0;
            this._parameters = [];
        }

        _isStar(operandInfo)
        {
            switch (operandInfo.kind) {
                case "LiteralContextDependentNumber":
                case "LiteralSpecConstantOpInteger":
                    // These types can be any width.
                    return true;
            }
            return operandInfo.quantifier && operandInfo.quantifier == "*";
        }

        nextComparisonType(operand)
        {
            if (this._operandInfoIndex >= this._operandInfos.length)
                throw new Error("Specified operand does not correspond to any that the instruction expects.");
            let operandInfo = this._operandInfos[this._operandInfoIndex];

            let isStar = this._isStar(operandInfo);

            if (this._parameters.length != 0) {
                let result = this._parameters[0];
                this._parameters.splice(0, 1);
                // FIXME: Handle parameters that require their own parameters
                ++this._operandIndex;
                if (this._parameters.length == 0 && !isStar)
                    ++this._operandInfoIndex;
                return result;
            }

            let composite = composites.get(operandInfo.kind);
            if (composite) {
                for (let base of composite.bases)
                    this._parameters.push(base);
                nextComparisonType(operand);
                return;
            }

            let kind = result.kinds[operandInfo.kind];
            if (kind) {
                let enumerant = kind[operand.enumerant];
                if (enumerant) {
                    let parameters = enumerant.parameters;
                    if (parameters) {
                        for (let parameter of parameters) {
                            this._parameters.push(parameter.kind);
                        }
                        ++this._operandIndex;
                        return operandInfo.kind;
                    }
                }
            }

            ++this._operandIndex;
            if (!isStar)
                ++this._operandInfoIndex;
            return operandInfo.kind;
        }

        check(operand)
        {
            matchType(this.nextComparisonType(operand), operand);
        }

        finalize()
        {
            if (this._parameters.length != 0)
                throw new Error("Operand not specified for parameter.");
            for (let i = this._operandInfoIndex; i < this._operandInfos.length; ++i) {
                let operandInfo = this._operandInfos[i];
                let quantifier = operandInfo.quantifier;
                if (quantifier != "?" && !this._isStar(operandInfo))
                    throw new Error("Did not specify operand " + i + " to instruction.");
            }
        }
    }

    for (let instruction of json.instructions) {
        if (!instruction.opname.startsWith("Op"))
            continue;
        let attributeName = instruction.opname.substring(2);
        result.ops[attributeName] = class {
            constructor(...operands)
            {
                let operandChecker = new OperandChecker(instruction.operands);
                for (let operand of operands)
                    operandChecker.check(operand);
                operandChecker.finalize();

                this._operands = operands;
            }
            get operands()
            {
                return this._operands;
            }
            get opname()
            {
                return instruction.opname;
            }
            get opcode()
            {
                return instruction.opcode;
            }
            get operandInfo()
            {
                return instruction.operands;
            }
            get storageSize()
            {
                let result = 1;
                for (let operand of this.operands) {
                    if (typeof operand == "number")
                        ++result;
                    else if (typeof operand == "string")
                        result += (((operand.length + 1) + 3) / 4) | 0;
                    else
                        ++result;
                }
                return result;
            }
            get largestId()
            {
                let maximumId = 0;
                let operandChecker = new OperandChecker(this.operandInfo);
                for (let operand of this.operands) {
                    let type = operandChecker.nextComparisonType(operand);
                    let idType = ids.get(type);
                    if (idType)
                        maximumId = Math.max(maximumId, operand);
                }
                return maximumId;
            }
        }
    }
    return result;
}