Skip to content

Instantly share code, notes, and snippets.

@zeux

zeux/fixupsimd.js

Last active Apr 3, 2020
Embed
What would you like to do?
A SIMD opcode remapper for the impending Wasm SIMD instruction renumbering, written on stream https://www.youtube.com/watch?v=zo20ryVj6e8
function fixupSimd(input) {
var data = new Uint8Array(input);
var read = 0;
var result = new Uint8Array(data.length * 2); // worst case: opcode renumbering changes 1-byte to 2-byte
var write = 0;
var readByte = function() {
read++;
if (read >= data.length) {
throw new Error("Out of bounds read");
}
return data[read - 1];
};
var readLEB = function() {
var result = 0;
var shift = 0;
var byte = 0;
do {
byte = readByte();
result |= (byte & 0x7f) << shift;
shift += 7;
}
while ((byte & 0x80) != 0);
return result;
};
var writeByte = function(byte) {
if (write >= result.length) {
throw new Error("Out of bounds write");
}
result[write++] = byte;
};
var writeLEB = function(value) {
do {
writeByte((value & 0x7f) | (value > 0x7f ? 0x80 : 0));
value >>= 7;
} while (value != 0);
};
var copyLEB = function() {
writeLEB(readLEB());
};
var copyBytes = function(count) {
for (var i = 0; i < count; ++i) {
result[write + i] = data[read + i];
}
read += count;
write += count;
};
var reserveLength = function() {
// 5 bytes are enough to represent 32 bit length
write += 5;
return write - 5;
};
var patchLength = function(start) {
var dstart = start + 5; // needs to match reserveLength
var length = write - dstart;
write = start;
writeLEB(length);
// not using copyWithin because it's not universally supported
for (var i = 0; i < length; ++i) {
result[write + i] = result[dstart + i];
}
write += length;
};
// magic + version
copyBytes(8);
while (read < data.length) {
var sid = readByte();
var slen = readLEB();
writeByte(sid);
// write non-code sections verbatim
if (sid != 10) {
writeLEB(slen);
copyBytes(slen);
continue;
}
var sstart = reserveLength();
var entries = readLEB();
writeLEB(entries);
for (var ei = 0; ei < entries; ++ei) {
var cstart = reserveLength();
var clen = readLEB();
var locals = readLEB();
writeLEB(locals);
for (var li = 0; li < locals; ++li) {
copyLEB();
copyLEB();
}
var depth = 0;
while (depth >= 0) {
var insn = readByte();
writeByte(insn);
if (insn == 0x02 || insn == 0x03 || insn == 0x04) {
writeByte(readByte());
depth++;
} else if (insn == 0x0b) {
depth--;
} else if (insn == 0x0c || insn == 0x0d || insn == 0x10 || (insn >= 0x20 && insn <= 0x24) || (insn >= 0x3f && insn <= 0x40)) {
copyLEB();
} else if (insn == 0x0e) {
var ln = readLEB();
writeLEB(ln);
for (var li = 0; li < ln; ++li) {
copyLEB();
}
copyLEB();
} else if (insn == 0x11 || (insn >= 0x28 && insn <= 0x3e)) {
copyLEB();
copyLEB();
} else if (insn == 0x41 || insn == 0x42) {
// work around issues with readLEB/writeLEB for signed 32-bit numbers and 64-bit numbers
var start = read;
readLEB();
var count = read - start;
read = start;
copyBytes(count);
} else if (insn == 0x43) {
copyBytes(4);
} else if (insn == 0x44) {
copyBytes(8);
} else if (insn == 0xfc) { // bulk memory extension
var ext = readByte();
writeByte(ext);
if (ext == 0x08 || ext == 0x0a || ext == 0x0c || ext == 0xe) {
copyLEB();
copyLEB();
} else if (ext == 0x09 || ext == 0x0b || ext == 0x0d) {
copyLEB();
}
} else if (insn == 0xfd) { // SIMD extension
var ext = readLEB();
// TODO: real remap goes here
if (ext == 0xc0) {
writeLEB(0x18);
} else if (ext == 0x18) {
writeLEB(0xc0);
} else {
writeLEB(ext);
}
if (ext == 0x00 || ext == 0x01 || (ext >= 0xd2 && ext <= 0xd7)) {
copyLEB();
copyLEB();
} else if (ext == 0x02 || ext == 0x03) {
copyBytes(16);
} else if ((ext >= 0x05 && ext <= 0x17) && ext != 0x08 && ext != 0x0c && ext != 0x0f && ext != 0x12 && ext != 0x15) {
copyLEB();
}
} else if (insn > 0xbf) {
throw new Error('Invalid instruction');
}
}
patchLength(cstart);
}
patchLength(sstart);
}
return result.buffer.slice(0, write);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment