Skip to content

Commit e7f61ca

Browse files
committed
Add MLIR test file (#1044)
1 parent 3164ab7 commit e7f61ca

File tree

3 files changed

+203
-6
lines changed

3 files changed

+203
-6
lines changed

source/mlir.js

+194-5
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,20 @@ const mlir = {};
77
mlir.ModelFactory = class {
88

99
match(context) {
10+
const stream = context.stream;
11+
if (stream && stream.length > 4) {
12+
const buffer = stream.peek(4);
13+
const signature = String.fromCharCode.apply(null, buffer);
14+
if (signature === 'ML\xEFR') {
15+
context.type = 'mlir.binary';
16+
return;
17+
}
18+
}
1019
try {
1120
const reader = context.read('text', 0x10000);
1221
for (let line = reader.read('\n'); line !== undefined; line = reader.read('\n')) {
1322
if (/module\s+(\w+\s+)?{/.test(line) || /tensor<\w+>/.test(line)) {
14-
context.type = 'mlir';
23+
context.type = 'mlir.text';
1524
return;
1625
}
1726
}
@@ -21,10 +30,22 @@ mlir.ModelFactory = class {
2130
}
2231

2332
async open(context) {
24-
const decoder = context.read('text.decoder');
25-
const parser = new mlir.Parser(decoder);
26-
const obj = parser.read();
27-
return new mlir.Model(obj);
33+
switch (context.type) {
34+
case 'mlir.text': {
35+
const decoder = context.read('text.decoder');
36+
const parser = new mlir.Parser(decoder);
37+
const obj = parser.read();
38+
return new mlir.Model(obj);
39+
}
40+
case 'mlir.binary': {
41+
const reader = new mlir.BytecodeReader(context);
42+
reader.read();
43+
throw new mlir.Error('Invalid content. File contains MLIR bytecode data.');
44+
}
45+
default: {
46+
throw new mlir.Error(`Unsupported MLIR format '${context.type}'.`);
47+
}
48+
}
2849
}
2950
};
3051

@@ -1168,6 +1189,174 @@ mlir.Utility = class {
11681189
}
11691190
};
11701191

1192+
mlir.BytecodeReader = class {
1193+
1194+
constructor(context) {
1195+
this._reader = new mlir.BinaryReader(context);
1196+
this._decoder = new TextDecoder('utf-8');
1197+
}
1198+
1199+
read() {
1200+
const reader = this._reader;
1201+
reader.read(4); // signature 'ML\xEFR'
1202+
this.version = reader.varint().toNumber();
1203+
this.producer = reader.string();
1204+
this.sections = [];
1205+
while (reader.position < reader.length) {
1206+
const code = reader.byte();
1207+
const identifier = code & 0x7F;
1208+
const length = reader.varint().toNumber();
1209+
if (code >> 7) {
1210+
const alignment = reader.varint();
1211+
reader.skip(alignment);
1212+
}
1213+
const next = reader.position + length;
1214+
switch (identifier) {
1215+
case 0: { // string
1216+
const lengths = new Array(reader.varint().toNumber());
1217+
for (let i = 0; i < lengths.length; i++) {
1218+
lengths[i] = reader.varint().toNumber();
1219+
}
1220+
this.strings = new Array(lengths.length);
1221+
for (let i = 0; i < this.strings.length; i++) {
1222+
const size = lengths[lengths.length - i - 1];
1223+
const buffer = reader.read(size);
1224+
this.strings[i] = this._decoder.decode(buffer);
1225+
}
1226+
break;
1227+
}
1228+
case 1: { // dialect
1229+
const numDialects = reader.varint().toNumber();
1230+
this.dialectNames = new Array(numDialects);
1231+
this.opNames = new Array(numDialects);
1232+
for (let i = 0; i < this.dialectNames.length; i++) {
1233+
const group = {};
1234+
const nameAndIsVersioned = reader.varint();
1235+
group.name = (nameAndIsVersioned >> 1n).toNumber();
1236+
if (nameAndIsVersioned & 1n) {
1237+
const size = reader.varint().toNumber();
1238+
group.version = reader.read(size);
1239+
}
1240+
this.dialectNames[i] = group;
1241+
}
1242+
for (let i = 0; i < this.opNames.length; i++) {
1243+
const dialect_ops_group = {};
1244+
dialect_ops_group.dialect = reader.varint();
1245+
dialect_ops_group.opNames = new Array(reader.varint().toNumber());
1246+
for (let j = 0; j < dialect_ops_group.opNames.length; j++) {
1247+
const op_name_group = {};
1248+
const nameAndIsRegistered = reader.varint();
1249+
op_name_group.isRegistered = (nameAndIsRegistered & 1n) === 1n;
1250+
op_name_group.name = (nameAndIsRegistered >> 1n).toNumber();
1251+
dialect_ops_group.opNames[j] = op_name_group;
1252+
}
1253+
this.opNames[i] = dialect_ops_group;
1254+
}
1255+
break;
1256+
}
1257+
case 2: // attrType
1258+
case 3: { // attrTypeOffset
1259+
/*
1260+
const numAttrs = reader.varint().toNumber();
1261+
const numTypes = reader.varint().toNumber();
1262+
for (let i = 0; i < (numAttrs + numTypes); i++) {
1263+
1264+
}
1265+
break;
1266+
*/
1267+
this.attrType = reader.stream(length);
1268+
break;
1269+
}
1270+
case 4: { // IR
1271+
reader.skip(length);
1272+
break;
1273+
}
1274+
case 5: { // resource
1275+
this.resource = reader.stream(length);
1276+
break;
1277+
}
1278+
case 6: { // resourceOffset
1279+
reader.skip(length);
1280+
break;
1281+
}
1282+
case 7: { // dialectVersions
1283+
reader.skip(length);
1284+
break;
1285+
}
1286+
case 8: { // properties
1287+
reader.skip(length);
1288+
break;
1289+
}
1290+
default: {
1291+
throw new mlir.Error(`Unsupported section identifier '${identifier}'.`);
1292+
}
1293+
}
1294+
if (reader.position !== next) {
1295+
throw new mlir.Error('Invalid section length.');
1296+
}
1297+
}
1298+
}
1299+
};
1300+
1301+
mlir.BinaryReader = class {
1302+
1303+
constructor(context) {
1304+
this._reader = context.read('binary');
1305+
}
1306+
1307+
get length() {
1308+
return this._reader.length;
1309+
}
1310+
1311+
get position() {
1312+
return this._reader.position;
1313+
}
1314+
1315+
skip(length) {
1316+
this._reader.skip(length);
1317+
}
1318+
1319+
read(length) {
1320+
return this._reader.read(length);
1321+
}
1322+
1323+
stream(length) {
1324+
return this._reader.stream(length);
1325+
}
1326+
1327+
byte() {
1328+
return this._reader.byte();
1329+
}
1330+
1331+
varint() {
1332+
let value = 0n;
1333+
let shift = 0n;
1334+
for (let i = 0; i < 10 && this._reader.position < this._reader.length; i++) {
1335+
const byte = this._reader.byte();
1336+
value |= BigInt(byte >> 1) << shift;
1337+
if ((byte & 1) === 1) {
1338+
return value;
1339+
}
1340+
shift += 7n;
1341+
}
1342+
throw new mlir.Error('Invalid varint value.');
1343+
}
1344+
1345+
string() {
1346+
const reader = this._reader;
1347+
let result = '';
1348+
let value = -1;
1349+
for (;;) {
1350+
value = reader.byte();
1351+
if (value === 0x00) {
1352+
break;
1353+
}
1354+
result += String.fromCharCode(value);
1355+
}
1356+
return result;
1357+
}
1358+
};
1359+
11711360
mlir.Error = class extends Error {
11721361

11731362
constructor(message) {

source/view.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5771,7 +5771,7 @@ view.ModelFactoryService = class {
57715771
this.register('./nnef', ['.nnef', '.dat']);
57725772
this.register('./onednn', ['.json']);
57735773
this.register('./espresso', ['.espresso.net', '.espresso.shape', '.espresso.weights'], ['.mlmodelc']);
5774-
this.register('./mlir', ['.mlir', '.mlir.txt']);
5774+
this.register('./mlir', ['.mlir', '.mlir.txt', '.mlirbc']);
57755775
this.register('./sentencepiece', ['.model']);
57765776
this.register('./hailo', ['.hn', '.har', '.metadata.json']);
57775777
this.register('./nnc', ['.nnc','.tflite']);

test/models.json

+8
Original file line numberDiff line numberDiff line change
@@ -3084,6 +3084,14 @@
30843084
"format": "MLIR",
30853085
"link": "https://github.com/lutzroeder/netron/issues/1044"
30863086
},
3087+
{
3088+
"type": "mlir",
3089+
"target": "versioned-op-2.0.mlirbc",
3090+
"source": "https://github.com/user-attachments/files/17174958/versioned-op-2.0.mlirbc.zip[versioned-op-2.0.mlirbc]",
3091+
"format": "MLIR",
3092+
"error": "Invalid content. File contains MLIR bytecode data.",
3093+
"link": "https://github.com/lutzroeder/netron/issues/1044"
3094+
},
30873095
{
30883096
"type": "mnn",
30893097
"target": "blazeface.mnn",

0 commit comments

Comments
 (0)