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