A SIMD opcode remapper for the impending Wasm SIMD instruction renumbering, written on stream https://www.youtube.com/watch?v=zo20ryVj6e8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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