Files
wehub-resource-sync 7254f7b4d1
Build / Build (macos-latest) (push) Has been cancelled
Build / Build (ubuntu-latest) (push) Has been cancelled
Build / Build (windows-latest) (push) Has been cancelled
Build / Analyze (javascript) (push) Has been cancelled
Build / Analyze (python) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:37:45 +08:00

1295 lines
47 KiB
JavaScript

// Experimental
const executorch = {};
const coreml = {};
const vulkan = {};
const xnnpack = {};
const qnn = {};
const ethosu = {};
const openvino = {};
const rockchip = {};
import * as base from './base.js';
import * as python from './python.js';
import * as pytorch from './pytorch.js';
executorch.ModelFactory = class {
async match(context) {
const reader = await executorch.Reader.open(context);
if (reader) {
return context.set('executorch', reader);
}
return null;
}
async open(context) {
executorch.schema = await context.require('./executorch-schema');
const target = context.value;
await target.read();
return new executorch.Model(target);
}
};
executorch.Model = class {
constructor(target) {
this.format = `ExecuTorch v${target.program.version}`;
this.modules = [];
for (const plan of target.program.execution_plan) {
for (const chain of plan.chains) {
const graph = new executorch.Graph(target, plan, chain);
this.modules.push(graph);
}
}
}
};
executorch.Graph = class {
constructor(target, plan, chain) {
this.name = plan.name || '';
this.inputs = [];
this.outputs = [];
this.nodes = [];
const values = new Map();
values.tensors = (index, items) => {
const list = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
const type = item ? new executorch.TensorType(item) : null;
let initializer = null;
if (item && item.data_buffer_idx > 0) {
initializer = new executorch.Tensor(item, target);
}
const identifier = items.length > 1 ? `${index}.${i}` : index.toString();
const value = new executorch.Value(identifier, type, initializer);
list.push(value);
}
return list;
};
values.map = (index, output) => {
if (output && values.has(index) && !Array.isArray(values.get(index).value)) {
const value = [new executorch.Value(index.toString(), null, null)];
values.set(index, { type: null, value });
}
if (!values.has(index)) {
const executorch_flatbuffer = executorch.schema.executorch_flatbuffer;
const val = plan.values[index].val;
const tensor = val instanceof executorch_flatbuffer.Tensor || val instanceof executorch_flatbuffer.TensorList || val instanceof executorch_flatbuffer.OptionalTensorList;
if (output && !tensor) {
const value = [new executorch.Value(index.toString(), null, null)];
values.set(index, { type: null, value });
} else if (val instanceof executorch_flatbuffer.Null) {
values.set(index, { type: 'attribute', value: null });
} else if (val instanceof executorch_flatbuffer.Int) {
values.set(index, { type: 'int64', value: val.int_val });
} else if (val instanceof executorch_flatbuffer.Bool) {
values.set(index, { type: 'int64', value: val.bool_val });
} else if (val instanceof executorch_flatbuffer.Double) {
values.set(index, { type: 'float64', value: val.double_val });
} else if (val instanceof executorch_flatbuffer.Tensor) {
const items = [val];
values.set(index, { type: null, value: values.tensors(index, items) });
} else if (val instanceof executorch_flatbuffer.String) {
values.set(index, { type: 'string', value: val.string_val });
} else if (val instanceof executorch_flatbuffer.IntList) {
const list = val.items.map((index) => plan.values[index].val.int_val);
values.set(index, { type: 'int64[]', value: list });
} else if (val instanceof executorch_flatbuffer.DoubleList) {
values.set(index, { type: 'float64[]', value: Array.from(val.items) });
} else if (val instanceof executorch_flatbuffer.BoolList) {
throw new executorch.Error('executorch_flatbuffer.BoolList not implemented.');
} else if (val instanceof executorch_flatbuffer.TensorList) {
const items = Array.from(val.items).map((arg) => arg === -1 ? null : plan.values[arg].val);
values.set(index, { type: null, value: values.tensors(index, items) });
} else if (val instanceof executorch_flatbuffer.OptionalTensorList) {
const items = Array.from(val.items).map((arg) => arg === -1 ? null : plan.values[arg].val);
values.set(index, { type: null, value: values.tensors(index, items) });
} else {
throw new Error(`Value type '${val.constructor.name}' not implemented.`);
}
}
return values.get(index);
};
for (let i = 0; i < plan.inputs.length; i++) {
const input = plan.inputs[i];
const value = values.map(input);
const name = plan.inputs.length === 1 ? 'input' : `input.${i}`;
const argument = new executorch.Argument(name, value.value, value.type);
this.inputs.push(argument);
}
for (let i = 0; i < plan.outputs.length; i++) {
const output = plan.outputs[i];
const value = values.map(output);
const name = plan.outputs.length === 1 ? 'output' : `output.${i}`;
const argument = new executorch.Argument(name, value.value, value.type);
this.outputs.push(argument);
}
const executorch_flatbuffer = executorch.schema.executorch_flatbuffer;
for (const instruction of chain.instructions) {
const instr_args = instruction.instr_args;
if (instr_args instanceof executorch_flatbuffer.JumpFalseCall ||
instr_args instanceof executorch_flatbuffer.MoveCall ||
instr_args instanceof executorch_flatbuffer.FreeCall) {
continue;
}
const node = new executorch.Node(target, plan, chain, instruction, values);
this.nodes.push(node);
}
}
};
executorch.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
executorch.Value = class Value {
constructor(name, type, initializer = null) {
if (typeof name !== 'string') {
throw new executorch.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = initializer && initializer.type ? initializer.type : type || null;
this.initializer = initializer;
}
};
executorch.Node = class {
constructor(target, plan, chain, instruction, values) {
this.name = '';
this.inputs = [];
this.outputs = [];
this.attributes = [];
const instr_args = instruction.instr_args;
const executorch_flatbuffer = executorch.schema.executorch_flatbuffer;
if (instr_args instanceof executorch_flatbuffer.KernelCall) {
const op = plan.operators[instr_args.op_index];
const name = op.name.split('::').pop();
const identifier = op.overload ? `${op.name}.${op.overload}` : op.name;
const torch = target.execution.__import__('torch');
const schemas = torch._C._jit_get_schemas_for_operator(op.name);
const schema = schemas.find((schema) => schema.name === op.name && schema.overload_name === op.overload);
if (!schema) {
throw new executorch.Error(`Operator schema for '${identifier}' not found.`);
}
const category = schema && schema.category ? schema.category : '';
const alias = (arg) => arg && arg.alias_info && arg.alias_info.before_set.length === 1 ? arg.alias_info.before_set[0] : null;
const outputs = new Set(schema && Array.isArray(schema.returns) ? schema.returns.map((arg) => alias(arg)).filter((alias) => alias !== null) : []);
const inputs = new Map();
this.type = { name, identifier, category };
let i = 0;
const args = instr_args.args;
for (; i < schema.arguments.length; i++) {
const index = args[i];
const arg = schema && i < schema.arguments.length ? schema.arguments[i] : null;
const output = arg ? alias(schema.arguments[i]) : null;
if (output && outputs.has(output)) {
inputs.set(output, index);
continue;
}
const name = arg ? arg.name : i.toString();
const value = values.map(index);
const argument = new executorch.Argument(name, value.value, value.type);
this.inputs.push(argument);
}
for (let j = 0; j < schema.returns.length; j++) {
const ret = schema.returns[j];
const output = alias(ret);
let index = args[i++];
index = output && inputs.has(output) ? inputs.get(output) : index;
const name = ret.name;
const value = values.map(index, true);
const argument = new executorch.Argument(name || '', value.value, value.type);
this.outputs.push(argument);
}
} else if (instr_args instanceof executorch_flatbuffer.DelegateCall) {
const delegate = plan.delegates[instr_args.delegate_index];
const args = instr_args.args;
if (!delegate.backend || !delegate.backend.type) {
throw new executorch.Error(`ExecuTorch delegate '${delegate.id}' not implemented.`);
}
this.type = delegate.backend.type;
const inputs = args.slice(0, this.type.inputs.length);
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i];
const value = values.map(input);
const name = inputs.length === 1 ? 'input' : `input.${i}`;
const argument = new executorch.Argument(name, value.value, value.type);
this.inputs.push(argument);
}
const outputs = args.slice(this.type.inputs.length, this.type.inputs.length + this.type.outputs.length);
for (let i = 0; i < outputs.length; i++) {
const output = outputs[i];
const value = values.map(output);
const name = outputs.length === 1 ? 'output' : `output.${i}`;
const argument = new executorch.Argument(name, value.value, value.type);
this.outputs.push(argument);
}
for (const spec of delegate.compile_specs) {
const value = spec.value instanceof Uint8Array ? new TextDecoder('utf-8').decode(spec.value) : spec.value;
const attribute = new executorch.Argument(spec.key, value, 'attribute');
this.attributes.push(attribute);
}
} else {
throw new Error(`Instruction type '${instr_args.constructor.name}' not implemented.`);
}
}
};
executorch.TensorType = class {
constructor(tensor) {
const ScalarType = executorch.schema.executorch_flatbuffer.ScalarType;
switch (tensor.scalar_type) {
case ScalarType.BYTE: this.dataType = 'uint8'; break;
case ScalarType.CHAR: this.dataType = 'int8'; break;
case ScalarType.SHORT: this.dataType = 'int16'; break;
case ScalarType.INT: this.dataType = 'int32'; break;
case ScalarType.LONG: this.dataType = 'int64'; break;
case ScalarType.HALF: this.dataType = 'float16'; break;
case ScalarType.FLOAT: this.dataType = 'float32'; break;
case ScalarType.DOUBLE: this.dataType = 'float64'; break;
case ScalarType.BFLOAT16: this.dataType = 'bfloat16'; break;
case 8: this.dataType = 'complex<float16>'; break;
case 9: this.dataType = 'complex<float32>'; break;
case 10: this.dataType = 'complex<float64>'; break;
case ScalarType.BOOL: this.dataType = 'boolean'; break;
case ScalarType.QINT8: this.dataType = 'qint8'; break;
case ScalarType.QUINT8: this.dataType = 'quint8'; break;
case ScalarType.QINT32: this.dataType = 'qint32'; break;
case 15: this.dataType = 'bfloat16'; break;
case ScalarType.QUINT4X2: this.dataType = 'quint4x2'; break;
case ScalarType.QUINT2X4: this.dataType = 'quint2x4'; break;
case 18: this.dataType = 'bits1x8'; break;
case 19: this.dataType = 'bits2x4'; break;
case 20: this.dataType = 'bits4x2'; break;
case 21: this.dataType = 'bits8'; break;
case ScalarType.BITS16: this.dataType = 'bits16'; break;
case ScalarType.FLOAT8E5M2: this.dataType = 'float8e5m2'; break;
case ScalarType.FLOAT8E4M3FN: this.dataType = 'float8e4m3fn'; break;
case ScalarType.FLOAT8E5M2FNUZ: this.dataType = 'float8e5m2fnuz'; break;
case ScalarType.FLOAT8E4M3FNUZ: this.dataType = 'float8e4m3fnuz'; break;
case ScalarType.UINT16: this.dataType = 'uint16'; break;
case ScalarType.UINT32: this.dataType = 'uint32'; break;
case ScalarType.UINT64: this.dataType = 'uint64'; break;
default: throw new executorch.Error(`Unknown tensor data type '${tensor.scalar_type}'.`);
}
this.shape = new executorch.TensorShape(Array.from(tensor.sizes));
}
toString() {
return this.dataType + this.shape.toString();
}
};
executorch.TensorShape = class {
constructor(dimensions = []) {
this.dimensions = dimensions;
}
toString() {
if (this.dimensions && this.dimensions.length > 0) {
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
return '';
}
};
executorch.Tensor = class {
constructor(tensor, target) {
this.type = new executorch.TensorType(tensor);
const data_buffer_idx = tensor.data_buffer_idx;
const program = target.program;
if (tensor.extra_tensor_info) {
throw new executorch.Error('Extra tensor info not implemented.');
} else if (Array.isArray(program.constant_buffer) && program.constant_buffer.length > 0) {
if (data_buffer_idx >= program.constant_buffer.length) {
throw new executorch.Error(`Constant buffer index out of range.`);
}
const buffer = program.constant_buffer[data_buffer_idx];
this.values = buffer.storage;
this.encoding = '<';
} else if (tensor.allocation_info === null) {
const constant_segment = program.constant_segment;
const data_segment = program.segments[constant_segment.segment_index];
const offset = constant_segment.offsets[data_buffer_idx];
let next = data_segment.size;
if (data_buffer_idx + 1 < constant_segment.offsets.length) {
next = constant_segment.offsets[data_buffer_idx + 1];
}
const size = next - offset;
const position = data_segment.offset + offset;
this.values = target.blob(position.toNumber(), size.toNumber());
this.encoding = '<';
} else {
throw new executorch.Error('Tensor allocation info not implemented.');
}
}
};
executorch.Reader = class {
static async open(context) {
const reader = await context.peek('flatbuffers.binary');
if (reader && reader.identifier === 'ET12') {
return new executorch.Reader(context, reader);
}
return null;
}
constructor(context, reader) {
this.context = context;
this.reader = reader;
}
async read() {
const context = this.context;
this.metadata = await pytorch.Metadata.open(context);
this.execution = new python.Execution();
this.metadata.register(this.execution);
const executorch_flatbuffer = executorch.schema.executorch_flatbuffer;
this.program = executorch_flatbuffer.Program.create(this.reader);
this.named_data = new Map();
if (this.program.named_data) {
this.named_data = new Map(this.program.named_data.map((entry) => [entry.key, entry.segment_index]));
}
this.reader = await context.read('binary');
if (this.reader.length >= 32) {
this.reader.seek(8);
const magic = String.fromCharCode(...this.reader.read(4));
if (magic === 'eh00') {
this.extended_file_header = {
length: this.reader.uint32(),
program_size: this.reader.uint64(),
segment_base_offset: this.reader.uint64(),
};
}
this.reader.seek(0);
}
for (const plan of this.program.execution_plan) {
for (const chain of plan.chains) {
for (const instruction of chain.instructions) {
const instr_args = instruction.instr_args;
if (instr_args instanceof executorch_flatbuffer.DelegateCall) {
const delegate = plan.delegates[instr_args.delegate_index];
if (delegate.backend) {
continue;
}
let data = null;
switch (delegate.processed.location) {
case executorch_flatbuffer.DataLocation.INLINE: {
data = this.program.backend_delegate_data[delegate.processed.index].data;
break;
}
case executorch_flatbuffer.DataLocation.SEGMENT: {
const segment = this.program.segments[delegate.processed.index];
const offset = segment.offset;
const size = segment.size;
data = this.blob(offset.toNumber(), size.toNumber());
break;
}
default: {
throw new executorch.Error(`Delegate data location '${delegate.processed.location}' not implemented.`);
}
}
switch (delegate.id) {
case 'XnnpackBackend':
delegate.backend = xnnpack.Reader.open(data, this);
break;
case 'CoreMLBackend':
delegate.backend = coreml.Reader.open(data, this);
break;
case 'VulkanBackend':
delegate.backend = vulkan.Reader.open(data, this);
break;
case 'QnnBackend':
delegate.backend = qnn.Reader.open(data, this);
break;
case 'EthosUBackend':
delegate.backend = ethosu.Reader.open(data, this);
break;
case 'OpenvinoBackend':
delegate.backend = openvino.Reader.open(data, this);
break;
case 'RockchipBackend':
delegate.backend = rockchip.Reader.open(data, this);
break;
default:
throw new executorch.Error(`ExecuTorch delegate '${delegate.id}' not implemented.`);
}
// eslint-disable-next-line no-await-in-loop
await delegate.backend.read();
}
}
}
}
}
blob(offset, size) {
if (this.extended_file_header) {
const segment_base_offset = this.extended_file_header.segment_base_offset;
this.reader.seek(segment_base_offset.toNumber() + offset);
const data = this.reader.read(size);
this.reader.seek(0);
return data;
}
return null;
}
segment(key) {
if (this.named_data.has(key)) {
const segment_index = this.named_data.get(key);
if (segment_index >= 0 && segment_index < this.program.segments.length) {
const segment = this.program.segments[segment_index];
const offset = segment.offset;
const size = segment.size;
return this.blob(offset.toNumber(), size.toNumber());
}
}
return null;
}
};
executorch.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading ExecuTorch model.';
}
};
xnnpack.Reader = class {
static open(data, target) {
if (data.length >= 30) {
const reader = base.BinaryReader.open(data);
reader.skip(4);
const magic = String.fromCharCode(...reader.read(4));
if (magic === 'XH00') {
return new xnnpack.Reader(reader, target);
}
}
return null;
}
constructor(reader, target) {
this.reader = reader;
this.target = target;
reader.skip(2);
this.flatbuffer = {
offset: reader.uint32(),
size: reader.uint32(),
};
this.constants = {
offset: reader.uint32(),
size: reader.uint32(),
};
}
async read() {
this.reader.seek(this.flatbuffer.offset);
const flatbuffers = await import('./flatbuffers.js');
const data = this.reader.read(this.flatbuffer.size);
const reader = flatbuffers.BinaryReader.open(data);
if (!executorch.schema.fb_xnnpack.XNNGraph.identifier(reader)) {
throw new xnnpack.Error('Invalid XNNPACK data.');
}
this.graph = executorch.schema.fb_xnnpack.XNNGraph.create(reader);
this.reader.seek(0);
const metadata = new xnnpack.Metadata();
this.type = new xnnpack.Graph(metadata, this.graph, this);
}
constant(idx) {
const constant_data = this.graph.constant_data[idx];
const named_key = constant_data.named_key;
if (named_key) {
return this.target.segment(named_key);
}
const offset = constant_data.offset;
const size = constant_data.size;
this.reader.seek(this.constants.offset + offset.toNumber());
const data = this.reader.read(size.toNumber());
this.reader.seek(0);
return data;
}
};
xnnpack.Graph = class {
constructor(metadata, graph, reader) {
this.name = 'XnnpackBackend';
this.type = 'graph';
this.inputs = [];
this.outputs = [];
this.nodes = [];
const values = new Map();
values.map = (id) => {
if (!values.has(id)) {
const fb_xnnpack = executorch.schema.fb_xnnpack;
const name = id.toString();
const xvalue = graph.xvalues[id].xvalue_union;
if (xvalue instanceof fb_xnnpack.XNNTensorValue) {
const type = new xnnpack.TensorType(xvalue);
const initializer = xvalue.constant_buffer_idx === 0 ? null : new xnnpack.Tensor(xvalue, reader);
const value = new xnnpack.Value(name, type, initializer);
values.set(id, value);
} else if (xvalue instanceof fb_xnnpack.XNNQuantizedTensorValue) {
const value = new xnnpack.Value(name, null, null);
values.set(id, value);
} else {
throw new xnnpack.Error(`Value type '${xvalue.constructor.name}' not implemented.`);
}
}
return values.get(id);
};
for (let i = 0; i < graph.input_ids.length; i++) {
const id = graph.input_ids[i];
const value = values.map(id);
const name = graph.input_ids.length === 1 ? 'input' : `input.${i}`;
const argument = new xnnpack.Argument(name, [value]);
this.inputs.push(argument);
}
for (let i = 0; i < graph.output_ids.length; i++) {
const id = graph.output_ids[i];
const value = values.map(id);
const name = graph.output_ids.length === 1 ? 'output' : `output.${i}`;
const argument = new xnnpack.Argument(name, [value]);
this.outputs.push(argument);
}
for (const xnode of graph.xnodes) {
const node = new xnnpack.Node(metadata, xnode, values);
this.nodes.push(node);
}
}
};
xnnpack.Node = class {
constructor(metadata, xnode, values) {
const node = xnode.xnode_union;
this.type = metadata.type(node.constructor.name) || { name: node.constructor.name };
this.name = '';
this.inputs = [];
this.outputs = [];
for (const [name, obj] of Object.entries(node)) {
let value = ArrayBuffer.isView(obj) ? Array.from(obj) : obj;
let type = 'attribute';
if (name.endsWith('_id')) {
value = obj === -1 || obj === 0xFFFFFFFF ? [] : [values.map(obj)];
type = null;
}
const argument = new xnnpack.Argument(name, value, type);
if (name === 'output_id') {
this.outputs.push(argument);
} else {
this.inputs.push(argument);
}
}
}
};
xnnpack.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
xnnpack.Value = class Value {
constructor(name, type, initializer = null) {
if (typeof name !== 'string') {
throw new executorch.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = initializer && initializer.type ? initializer.type : type || null;
this.initializer = initializer;
}
};
xnnpack.Metadata = class {
constructor() {
this._types = new Map();
this.register('_XNNCat', 'Tensor');
this.register('_XNNNodeConv', 'Layer');
this.register('XNNArgMaxPooling2d', 'Pool');
this.register('XNNAvgPooling2d', 'Pool');
this.register('XNNCeiling', 'Activation');
this.register('XNNConcatenate2', 'Tensor');
this.register('XNNConcatenate3', 'Tensor');
this.register('XNNConcatenate4', 'Tensor');
this.register('XNNConcatenate5', 'Tensor');
this.register('XNNConv2d', 'Layer');
this.register('XNNConvTranspose2d', 'Layer');
this.register('XNNDepthwiseConv2d', 'Layer');
this.register('XNNELU', 'Activation');
this.register('XNNFullyConnected', 'Layer');
this.register('XNNGelu', 'Activation');
this.register('XNNGlobalAvgPooling2d', 'Pool');
this.register('XNNGlobalAvgPooling2d', 'Pool');
this.register('XNNHardswish', 'Activation');
this.register('XNNLeakyReLU', 'Activation');
this.register('XNNMaxPooling2d', 'Pool');
this.register('XNNPReLU', 'Activation');
this.register('XNNSigmoid', 'Activation');
this.register('XNNSoftmax', 'Activation');
this.register('XNNTanh', 'Activation');
this.register('XNNStaticTranspose', 'Transform');
}
register(name, category) {
this._types.set(name, { name, category });
}
type(name) {
return this._types.get(name);
}
};
xnnpack.TensorType = class {
constructor(tensor) {
xnnpack.TensorType._types = executorch.TensorType._types || [
'invalid', 'float32', 'float16',
'qint8', 'quint8', 'qint32',
'qcint8', 'qcint32', 'qcint4',
'qdint8', 'qbint4', 'qpint8',
'int32', 'pfp32', 'bfloat16'
];
if (tensor.datatype >= xnnpack.TensorType._types.length) {
throw new xnnpack.Error(`Unknown tensor data type '${tensor.datatype}'.`);
}
this.dataType = xnnpack.TensorType._types[tensor.datatype];
this.shape = new xnnpack.TensorShape(Array.from(tensor.dims));
}
toString() {
return this.dataType + this.shape.toString();
}
};
xnnpack.TensorShape = class {
constructor(dimensions = []) {
this.dimensions = dimensions;
}
toString() {
if (this.dimensions && this.dimensions.length > 0) {
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
return '';
}
};
xnnpack.Tensor = class {
constructor(tensor, reader) {
this.type = new xnnpack.TensorType(tensor);
this.values = reader.constant(tensor.constant_buffer_idx);
this.encoding = '<';
}
};
xnnpack.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading XNNPACK model.';
}
};
vulkan.Reader = class {
static open(data, target) {
if (data.length >= 30) {
const reader = base.BinaryReader.open(data);
reader.skip(4);
const magic = String.fromCharCode(...reader.read(4));
if (magic === 'VH00') {
return new vulkan.Reader(reader, target);
}
}
return null;
}
constructor(reader, target) {
this.reader = reader;
this.target = target;
reader.skip(2);
this.flatbuffer = {
offset: reader.uint32(),
size: reader.uint32(),
};
this.constants = {
offset: reader.uint32(),
size: reader.uint32(),
};
}
async read() {
this.reader.seek(this.flatbuffer.offset);
const metadata = new vulkan.Metadata(this.target.execution);
const flatbuffers = await import('./flatbuffers.js');
const data = this.reader.read(this.flatbuffer.size);
const reader = flatbuffers.BinaryReader.open(data);
if (!executorch.schema.vkgraph.VkGraph.identifier(reader)) {
throw new xnnpack.Error('Invalid Vuklan data.');
}
this.graph = executorch.schema.vkgraph.VkGraph.create(reader);
this.reader.seek(0);
this.type = new vulkan.Graph(metadata, this.graph, this);
}
constant(id) {
const constant = this.graph.constants[id];
if (constant.named_key && constant.offset === 0xffffffffffffffffn) {
return this.target.segment(constant.named_key);
}
const offset = constant.offset;
const length = constant.length;
this.reader.seek(this.constants.offset + offset.toNumber());
const data = this.reader.read(length.toNumber());
this.reader.seek(0);
return data;
}
};
vulkan.Graph = class {
constructor(metadata, graph, reader) {
this.name = 'VulkanBackend';
this.inputs = [];
this.outputs = [];
this.nodes = [];
const values = new Map();
values.map = (id) => {
if (!values.has(id)) {
const vkgraph = executorch.schema.vkgraph;
const arg = graph.values[id].value;
if (arg instanceof vkgraph.VkTensor) {
const type = new vulkan.TensorType(arg);
const initializer = arg.constant_id === -1 ? null : new vulkan.Tensor(arg, reader);
const value = new vulkan.Value(id.toString(), type, initializer);
values.set(id, { type: null, value: [value] });
} else if (arg instanceof vkgraph.Int) {
values.set(id, { type: 'int64', value: arg.int_val });
} else if (arg instanceof vkgraph.IntList) {
values.set(id, { type: 'int64[]', value: Array.from(arg.items) });
} else if (arg instanceof vkgraph.Double) {
values.set(id, { type: 'float64', value: arg.double_val });
} else if (arg instanceof vkgraph.Bool) {
values.set(id, { type: 'boolean', value: arg.bool_val });
} else if (arg instanceof vkgraph.Null) {
values.set(id, { type: 'attribute', value: null });
} else {
throw new Error(`Value type '${arg.constructor.name}' not implemented.`);
}
}
return values.get(id);
};
for (let i = 0; i < graph.input_ids.length; i++) {
const id = graph.input_ids[i];
const value = values.map(id);
const name = graph.input_ids.length === 1 ? 'input' : `input.${i}`;
const argument = new vulkan.Argument(name, value.value, value.type);
this.inputs.push(argument);
}
for (let i = 0; i < graph.output_ids.length; i++) {
const id = graph.output_ids[i];
const value = values.map(id);
const name = graph.output_ids.length === 1 ? 'output' : `output.${i}`;
const argument = new vulkan.Argument(name, value.value, value.type);
this.outputs.push(argument);
}
for (const op of graph.chain) {
const node = new vulkan.Node(metadata, op, values);
this.nodes.push(node);
}
}
};
vulkan.Node = class {
constructor(metadata, op, values) {
const schema = metadata.type(op.name);
if (!schema) {
throw new vulkan.Error(`Operator schema for '${op.name}' not found.`);
}
this.type = {
name: op.name.split(/\.([^.]*)$/)[0],
identifier: op.name,
category: schema.category || ''
};
this.name = op.node_id.toString();
this.inputs = [];
this.outputs = [];
this.attributes = [];
for (let i = 0; i < op.args.length; i++) {
const arg = op.args[i];
const input = schema && i < schema.arguments.length;
const def = input ? schema.arguments[i] : schema.returns[i - schema.arguments.length];
const value = values.map(arg);
const argument = new vulkan.Argument(def.name || '', value.value, value.type);
if (input) {
this.inputs.push(argument);
} else {
this.outputs.push(argument);
}
}
}
};
vulkan.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
vulkan.Value = class Value {
constructor(name, type, initializer = null) {
if (typeof name !== 'string') {
throw new executorch.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = initializer && initializer.type ? initializer.type : type || null;
this.initializer = initializer;
}
};
vulkan.TensorType = class {
constructor(tensor) {
const types = ['bool', 'uint8', 'int8', 'int32', 'float16', 'float32', 'float64', 'int64'];
if (tensor.datatype >= types.length) {
throw new vulkan.Error(`Unknown tensor data type '${tensor.datatype}'.`);
}
this.dataType = types[tensor.datatype];
this.shape = new vulkan.TensorShape(Array.from(tensor.dims));
const vkgraph = executorch.schema.vkgraph;
if (tensor.memory_layout !== vkgraph.VkMemoryLayout.DEFAULT_LAYOUT) {
this.denotation = vkgraph.VkMemoryLayout[tensor.memory_layout];
if (!this.denotation) {
throw new vulkan.Error(`Unsupported memory layout '${tensor.memory_layout}'.`);
}
}
if (tensor.storage_type !== vkgraph.VkStorageType.DEFAULT_STORAGE) {
this.layout = vkgraph.VkStorageType[tensor.storage_type];
if (!this.layout) {
throw new vulkan.Error(`Unsupported storage type '${tensor.storage_type}'.`);
}
}
}
toString() {
return this.dataType + this.shape.toString();
}
};
vulkan.TensorShape = class {
constructor(dimensions = []) {
this.dimensions = dimensions;
}
toString() {
if (this.dimensions && this.dimensions.length > 0) {
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
return '';
}
};
vulkan.Tensor = class {
constructor(tensor, reader) {
this.type = new vulkan.TensorType(tensor);
this.values = reader.constant(tensor.constant_id);
this.encoding = '<';
}
};
vulkan.Metadata = class {
constructor(execution) {
this.execution = execution;
}
register(signature) {
const torch = this.execution.__import__('torch');
const registry = torch._C.getRegistry();
const schema = torch.FunctionSchema.parse(signature);
const op = new torch._C.Operator(schema);
registry.registerOperator(op);
}
type(identifier) {
identifier = identifier.split(/\.([^.]*)$/);
let name = identifier[0].replace('.', '::');
if (name.indexOf('::') === -1) {
name = `et_vk::${name}`;
}
const overload = identifier[1] === 'default' ? '' : identifier[1];
const torch = this.execution.__import__('torch');
const schemas = torch._C._jit_get_schemas_for_operator(name);
const schema = schemas.find((schema) => schema.name === name && schema.overload_name === overload);
return schema;
}
};
vulkan.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Vulkan model.';
}
};
coreml.Reader = class {
static open(data, target) {
const reader = base.BinaryReader.open(data);
return new coreml.Reader(reader, target);
}
constructor(reader, target) {
this.reader = reader;
this.target = target;
}
async factory() {
const coreml = await import('./coreml.js');
return new coreml.ModelFactory();
}
async read() {
const entries = this.entries(this.reader);
const factory = await this.factory();
const streams = new Map();
for (const [path, location] of entries) {
streams.set(path, this.stream(location.offset, location.size));
}
const context = this.target.context;
for (const [key] of streams) {
const content = context.context(key, streams.get(key), streams);
// eslint-disable-next-line no-await-in-loop
const type = await factory.match(content);
if (type === 'coreml.manifest') {
// eslint-disable-next-line no-await-in-loop
const model = await factory.open(content);
[this.type] = model.modules;
this.type.name = 'CoreMLBackend';
return;
}
}
}
stream(offset, size) {
this.reader.seek(offset);
const stream = this.reader.stream(size);
this.reader.seek(0);
return stream;
}
entries(reader) {
const files = new Map();
reader.seek(reader.length - 1);
const str = [];
let depth = 0;
do {
const c = String.fromCharCode(reader.byte());
reader.skip(-2);
if (c === '{') {
depth++;
} else if (c === '}') {
depth--;
}
str.push(c);
} while (depth > 0);
const metadata = JSON.parse(str.join(''));
const nodes = metadata.nodes;
const roots = Array.from(nodes);
for (const root of roots) {
if (root !== null) {
for (const index of Object.values(root.children)) {
roots[index] = null;
}
}
}
const process = (path, node) => {
path = path ? `${path}/${node.name}` : node.name;
if (node.kind === 0) {
files.set(path, node.dataRegion);
} else if (node.kind === 1) {
for (const index of Object.values(node.children)) {
process(path, nodes[index]);
}
} else {
throw new Error(`Node kind '${node.kind}' not implemented.`);
}
};
for (const root of roots.filter((node) => node !== null)) {
process('', root);
}
return files;
}
};
qnn.Reader = class {
static open(data, target) {
if (data.length >= 20) {
const reader = base.BinaryReader.open(data);
const magic = reader.uint32();
if (magic === 0x5678ABCD) {
return new qnn.Reader(reader, target);
}
}
return null;
}
constructor(reader, target) {
this.reader = reader;
this.target = target;
this.signature = reader.uint64();
this.size = reader.uint64();
}
async read() {
// https://github.com/pytorch/executorch/blob/main/backends/qualcomm/runtime/backends/QnnCustomProtocol.h
throw new executorch.Error('Undocumented QNN backend not implemented.');
}
};
qnn.Graph = class {
constructor() {
this.name = 'QnnBackend';
this.inputs = [];
this.outputs = [];
this.nodes = [];
}
};
ethosu.Reader = class {
static open(data /* , target */) {
if (data.length >= 32) {
const reader = base.BinaryReader.open(data);
const magicBuffer = reader.read(16);
const magic = String.fromCharCode(...magicBuffer).replace(/\0/g, '');
if (magic === 'vela_bin_stream') {
return new ethosu.Reader(reader, data.length);
}
}
return null;
}
constructor(reader, size) {
this.reader = reader;
this.size = size;
}
async read() {
this.reader.seek(0);
const blocks = new Map();
while (this.reader.position < this.size) {
const nameBuffer = this.reader.read(16);
const name = String.fromCharCode(...nameBuffer).replace(/\0/g, '');
const size = this.reader.uint32();
this.reader.skip(12);
const data = this.reader.read(size);
blocks.set(name, data);
const padding = (16 - (size % 16)) % 16;
this.reader.skip(padding);
if (name === 'vela_end_stream') {
break;
}
}
const args = (data) => {
if (data && data.length >= 4) {
const reader = base.BinaryReader.open(data);
const count = reader.int32();
const arg = [];
for (let i = 0; i < count; i++) {
const shape = [];
for (let j = 0; j < 6; j++) {
shape.push(reader.int32());
}
const elem_size = reader.int32();
const offset = reader.int32();
const region = reader.int32();
arg.push({ shape, elem_size, offset, region });
}
return arg;
}
return [];
};
const inputs = args(blocks.get('inputs'));
const outputs = args(blocks.get('outputs'));
this.type = new ethosu.Graph(inputs, outputs);
}
};
ethosu.Graph = class {
constructor(inputs, outputs) {
this.name = 'EthosUBackend';
this.inputs = [];
this.outputs = [];
this.nodes = [];
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i];
const type = new ethosu.TensorType(input);
const value = new ethosu.Value(i.toString(), type, null);
const name = inputs.length === 1 ? 'input' : `input.${i}`;
const argument = new ethosu.Argument(name, [value]);
this.inputs.push(argument);
}
for (let i = 0; i < outputs.length; i++) {
const output = outputs[i];
const type = new ethosu.TensorType(output);
const value = new ethosu.Value((inputs.length + i).toString(), type, null);
const name = outputs.length === 1 ? 'output' : `output.${i}`;
const argument = new ethosu.Argument(name, [value]);
this.outputs.push(argument);
}
}
};
ethosu.Argument = class {
constructor(name, value, type = null, visible = true) {
this.name = name;
this.value = value;
this.type = type;
this.visible = visible;
}
};
ethosu.Value = class Value {
constructor(name, type, initializer = null) {
if (typeof name !== 'string') {
throw new executorch.Error(`Invalid value identifier '${JSON.stringify(name)}'.`);
}
this.name = name;
this.type = initializer && initializer.type ? initializer.type : type || null;
this.initializer = initializer;
}
};
ethosu.TensorType = class {
constructor(io) {
switch (io.elem_size) {
case 1: this.dataType = 'int8'; break;
case 2: this.dataType = 'int16'; break;
case 4: this.dataType = 'int32'; break;
default: this.dataType = `?${io.elem_size}`; break;
}
const shape = io.shape.filter((dim, index) => dim !== 1 || index === io.shape.length - 1 || io.shape.slice(index).some((d) => d !== 1));
this.shape = new ethosu.TensorShape(shape.length > 0 ? shape : [1]);
}
toString() {
return this.dataType + this.shape.toString();
}
};
ethosu.TensorShape = class {
constructor(dimensions = []) {
this.dimensions = dimensions;
}
toString() {
if (this.dimensions && this.dimensions.length > 0) {
return `[${this.dimensions.map((dimension) => dimension.toString()).join(',')}]`;
}
return '';
}
};
ethosu.Error = class extends Error {
constructor(message) {
super(message);
this.name = 'Error loading Ethos-U model.';
}
};
openvino.Reader = class {
static open(data /* , target */) {
return new openvino.Reader(data);
}
constructor(data) {
this.data = data;
}
async read() {
throw new executorch.Error('OpenVINO backend not implemented.');
}
};
openvino.Graph = class {
constructor() {
this.name = 'OpenvinoBackend';
this.inputs = [];
this.outputs = [];
this.nodes = [];
}
};
rockchip.Reader = class {
static open(data /* , target */) {
return new rockchip.Reader(data);
}
constructor(data) {
this.data = data;
}
async read() {
throw new executorch.Error('Rockchip backend not implemented.');
}
};
rockchip.Graph = class {
constructor() {
this.name = 'RockchipBackend';
this.inputs = [];
this.outputs = [];
this.nodes = [];
}
};
export const ModelFactory = executorch.ModelFactory;