wasm gc: fix issues in disassembler

This commit is contained in:
Alexey Andreev 2024-08-30 12:54:39 +02:00
parent 8ba4f9c30b
commit 0b11a9ce90
9 changed files with 172 additions and 17 deletions

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.wasm.disasm; package org.teavm.backend.wasm.disasm;
import com.carrotsearch.hppc.IntArrayList;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@ -23,6 +24,8 @@ import java.nio.file.Files;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.teavm.backend.wasm.parser.AddressListener; import org.teavm.backend.wasm.parser.AddressListener;
import org.teavm.backend.wasm.parser.CodeSectionParser; import org.teavm.backend.wasm.parser.CodeSectionParser;
import org.teavm.backend.wasm.parser.FunctionSectionListener;
import org.teavm.backend.wasm.parser.FunctionSectionParser;
import org.teavm.backend.wasm.parser.GlobalSectionParser; import org.teavm.backend.wasm.parser.GlobalSectionParser;
import org.teavm.backend.wasm.parser.ImportSectionListener; import org.teavm.backend.wasm.parser.ImportSectionListener;
import org.teavm.backend.wasm.parser.ImportSectionParser; import org.teavm.backend.wasm.parser.ImportSectionParser;
@ -30,11 +33,14 @@ import org.teavm.backend.wasm.parser.ModuleParser;
import org.teavm.backend.wasm.parser.NameSectionListener; import org.teavm.backend.wasm.parser.NameSectionListener;
import org.teavm.backend.wasm.parser.NameSectionParser; import org.teavm.backend.wasm.parser.NameSectionParser;
import org.teavm.backend.wasm.parser.TypeSectionParser; import org.teavm.backend.wasm.parser.TypeSectionParser;
import org.teavm.backend.wasm.parser.WasmHollowFunctionType;
import org.teavm.common.AsyncInputStream; import org.teavm.common.AsyncInputStream;
import org.teavm.common.ByteArrayAsyncInputStream; import org.teavm.common.ByteArrayAsyncInputStream;
public final class Disassembler { public final class Disassembler {
private DisassemblyWriter writer; private DisassemblyWriter writer;
private WasmHollowFunctionType[] functionTypes;
private int[] functionTypeRefs;
public Disassembler(DisassemblyWriter writer) { public Disassembler(DisassemblyWriter writer) {
this.writer = writer; this.writer = writer;
@ -104,6 +110,7 @@ public final class Disassembler {
writer.setAddressOffset(pos); writer.setAddressOffset(pos);
var sectionParser = new TypeSectionParser(typeWriter); var sectionParser = new TypeSectionParser(typeWriter);
sectionParser.parse(writer.addressListener, bytes); sectionParser.parse(writer.addressListener, bytes);
functionTypes = typeWriter.getFunctionTypes();
writer.flush(); writer.flush();
}; };
} else if (code == 2) { } else if (code == 2) {
@ -111,19 +118,35 @@ public final class Disassembler {
var parser = new ImportSectionParser(importListener); var parser = new ImportSectionParser(importListener);
parser.parse(AddressListener.EMPTY, bytes); parser.parse(AddressListener.EMPTY, bytes);
}; };
} else if (code == 3) {
return bytes -> {
var signatures = new IntArrayList();
for (var i = 0; i < importListener.count; ++i) {
signatures.add(0);
}
var parser = new FunctionSectionParser(new FunctionSectionListener() {
@Override
public void function(int index, int typeIndex) {
signatures.add(typeIndex);
}
});
parser.parse(AddressListener.EMPTY, bytes);
functionTypeRefs = signatures.toArray();
};
} else if (code == 6) { } else if (code == 6) {
return bytes -> { return bytes -> {
writer.write("(; global section size: " + bytes.length + " ;)").eol(); writer.write("(; global section size: " + bytes.length + " ;)").eol();
var globalWriter = new DisassemblyGlobalSectionListener(writer, nameProvider); var globalWriter = new DisassemblyGlobalSectionListener(writer, nameProvider);
writer.setAddressOffset(pos); writer.setAddressOffset(pos);
var sectionParser = new GlobalSectionParser(globalWriter); var sectionParser = new GlobalSectionParser(globalWriter);
sectionParser.setFunctionIndexOffset(importListener.count);
sectionParser.parse(writer.addressListener, bytes); sectionParser.parse(writer.addressListener, bytes);
writer.flush(); writer.flush();
}; };
} else if (code == 10) { } else if (code == 10) {
return bytes -> { return bytes -> {
var disassembler = new DisassemblyCodeSectionListener(writer, nameProvider); var disassembler = new DisassemblyCodeSectionListener(writer, nameProvider);
disassembler.setFunctionTypes(functionTypes);
disassembler.setFunctionTypeRefs(functionTypeRefs);
writer.setAddressOffset(pos); writer.setAddressOffset(pos);
writer.write("(; code section size: " + bytes.length + " ;)").eol(); writer.write("(; code section size: " + bytes.length + " ;)").eol();
var sectionParser = new CodeSectionParser(disassembler); var sectionParser = new CodeSectionParser(disassembler);

View File

@ -17,17 +17,29 @@ package org.teavm.backend.wasm.disasm;
import org.teavm.backend.wasm.parser.CodeListener; import org.teavm.backend.wasm.parser.CodeListener;
import org.teavm.backend.wasm.parser.CodeSectionListener; import org.teavm.backend.wasm.parser.CodeSectionListener;
import org.teavm.backend.wasm.parser.WasmHollowFunctionType;
import org.teavm.backend.wasm.parser.WasmHollowType; import org.teavm.backend.wasm.parser.WasmHollowType;
public class DisassemblyCodeSectionListener extends BaseDisassemblyListener implements CodeSectionListener { public class DisassemblyCodeSectionListener extends BaseDisassemblyListener implements CodeSectionListener {
private int currentFunctionId; private int currentFunctionId;
private int currentFunctionParameterCount;
private DisassemblyCodeListener codeListener; private DisassemblyCodeListener codeListener;
private WasmHollowFunctionType[] functionTypes;
private int[] functionTypeRefs;
public DisassemblyCodeSectionListener(DisassemblyWriter writer, NameProvider nameProvider) { public DisassemblyCodeSectionListener(DisassemblyWriter writer, NameProvider nameProvider) {
super(writer, nameProvider); super(writer, nameProvider);
codeListener = new DisassemblyCodeListener(writer, nameProvider); codeListener = new DisassemblyCodeListener(writer, nameProvider);
} }
public void setFunctionTypes(WasmHollowFunctionType[] functionTypes) {
this.functionTypes = functionTypes;
}
public void setFunctionTypeRefs(int[] functionTypeRefs) {
this.functionTypeRefs = functionTypeRefs;
}
@Override @Override
public boolean functionStart(int index, int size) { public boolean functionStart(int index, int size) {
currentFunctionId = index; currentFunctionId = index;
@ -37,7 +49,38 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl
if (name != null) { if (name != null) {
writer.write(" $").write(name); writer.write(" $").write(name);
} }
writer.endLinkTarget().indent().eol(); writer.endLinkTarget();
var typeRef = functionTypeRefs[index];
writer.write(" (type ");
writeTypeRef(typeRef);
writer.write(")");
writer.indent().eol();
var type = typeRef < functionTypes.length ? functionTypes[typeRef] : null;
if (type != null) {
currentFunctionParameterCount = type.parameterTypes.length;
for (var i = 0; i < type.parameterTypes.length; ++i) {
writer.write("(param ");
writer.startLinkTarget("l" + index + "." + i).write(" (; " + i + " ;)");
var paramName = nameProvider.local(index, i);
if (paramName != null) {
writer.write(" $").write(paramName);
}
writer.endLinkTarget();
writer.write(" ");
writeType(type.parameterTypes[i]);
writer.write(")").eol();
}
for (var i = 0; i < type.returnTypes.length; ++i) {
writer.write("(result ");
writeType(type.returnTypes[i]);
writer.write(")").eol();
}
} else {
currentFunctionParameterCount = 0;
}
return true; return true;
} }
@ -51,7 +94,7 @@ public class DisassemblyCodeSectionListener extends BaseDisassemblyListener impl
writer.address(); writer.address();
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
writer.write("(local "); writer.write("(local ");
var id = i + start; var id = i + start + currentFunctionParameterCount;
writer.startLinkTarget("l" + currentFunctionId + "." + id).write("(; " + id + " ;)"); writer.startLinkTarget("l" + currentFunctionId + "." + id).write("(; " + id + " ;)");
var name = nameProvider.local(currentFunctionId, id); var name = nameProvider.local(currentFunctionId, id);
if (name != null) { if (name != null) {

View File

@ -15,7 +15,10 @@
*/ */
package org.teavm.backend.wasm.disasm; package org.teavm.backend.wasm.disasm;
import java.util.ArrayList;
import java.util.List;
import org.teavm.backend.wasm.parser.TypeSectionListener; import org.teavm.backend.wasm.parser.TypeSectionListener;
import org.teavm.backend.wasm.parser.WasmHollowFunctionType;
import org.teavm.backend.wasm.parser.WasmHollowStorageType; import org.teavm.backend.wasm.parser.WasmHollowStorageType;
import org.teavm.backend.wasm.parser.WasmHollowType; import org.teavm.backend.wasm.parser.WasmHollowType;
@ -25,6 +28,9 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl
private int currentTypeIndex; private int currentTypeIndex;
private int fieldIndex; private int fieldIndex;
private boolean needsFieldIndex; private boolean needsFieldIndex;
private List<WasmHollowFunctionType> functionTypes = new ArrayList<>();
private List<WasmHollowType> parameterTypes = new ArrayList<>();
private List<WasmHollowType> resultTypes = new ArrayList<>();
public DisassemblyTypeSectionListener(DisassemblyWriter writer, NameProvider nameProvider) { public DisassemblyTypeSectionListener(DisassemblyWriter writer, NameProvider nameProvider) {
super(writer, nameProvider); super(writer, nameProvider);
@ -42,6 +48,7 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl
@Override @Override
public void startType(int index, boolean open, int[] supertypes) { public void startType(int index, boolean open, int[] supertypes) {
functionTypes.add(null);
currentTypeIndex = index; currentTypeIndex = index;
writer.address().write("(type "); writer.address().write("(type ");
writer.startLinkTarget("t" + index).write("(; ").write(String.valueOf(index)).write(" ;) "); writer.startLinkTarget("t" + index).write("(; ").write(String.valueOf(index)).write(" ;) ");
@ -119,6 +126,11 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl
@Override @Override
public void resultType(WasmHollowType type) { public void resultType(WasmHollowType type) {
if (emittingReturn) {
resultTypes.add(type);
} else {
parameterTypes.add(type);
}
writer.address().write("(").write(emittingReturn ? "result" : "param").write(" "); writer.address().write("(").write(emittingReturn ? "result" : "param").write(" ");
writeType(type); writeType(type);
writer.write(")").eol(); writer.write(")").eol();
@ -126,6 +138,11 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl
@Override @Override
public void endFuncType() { public void endFuncType() {
var type = new WasmHollowFunctionType(parameterTypes.toArray(new WasmHollowType[0]),
resultTypes.toArray(new WasmHollowType[0]));
functionTypes.set(currentTypeIndex, type);
parameterTypes.clear();
resultTypes.clear();
emittingReturn = false; emittingReturn = false;
writer.outdent().write(")").eol(); writer.outdent().write(")").eol();
} }
@ -139,4 +156,8 @@ public class DisassemblyTypeSectionListener extends BaseDisassemblyListener impl
} }
writer.write(")").eol(); writer.write(")").eol();
} }
public WasmHollowFunctionType[] getFunctionTypes() {
return functionTypes.toArray(new WasmHollowFunctionType[0]);
}
} }

View File

@ -31,16 +31,11 @@ import org.teavm.backend.wasm.model.expression.WasmSignedType;
public class CodeParser extends BaseSectionParser { public class CodeParser extends BaseSectionParser {
private CodeListener codeListener; private CodeListener codeListener;
private final List<Block> blockStack = new ArrayList<>(); private final List<Block> blockStack = new ArrayList<>();
private int functionIndexOffset;
public void setCodeListener(CodeListener codeListener) { public void setCodeListener(CodeListener codeListener) {
this.codeListener = codeListener; this.codeListener = codeListener;
} }
public void setFunctionIndexOffset(int functionIndexOffset) {
this.functionIndexOffset = functionIndexOffset;
}
@Override @Override
protected void parseContent() { protected void parseContent() {
parseCode(); parseCode();
@ -104,7 +99,7 @@ public class CodeParser extends BaseSectionParser {
codeListener.opcode(Opcode.RETURN); codeListener.opcode(Opcode.RETURN);
break; break;
case 0x10: case 0x10:
codeListener.call(readLEB() + functionIndexOffset); codeListener.call(readLEB());
break; break;
case 0x11: case 0x11:
codeListener.indirectCall(readLEB(), readLEB()); codeListener.indirectCall(readLEB(), readLEB());
@ -598,7 +593,7 @@ public class CodeParser extends BaseSectionParser {
break; break;
case 0xD2: case 0xD2:
codeListener.functionReference(readLEB() + functionIndexOffset); codeListener.functionReference(readLEB());
break; break;
case 0xD3: case 0xD3:

View File

@ -49,7 +49,6 @@ public class CodeSectionParser extends BaseSectionParser {
var codeListener = listener.code(); var codeListener = listener.code();
if (codeListener != null) { if (codeListener != null) {
codeParser.setCodeListener(codeListener); codeParser.setCodeListener(codeListener);
codeParser.setFunctionIndexOffset(functionIndexOffset);
codeParser.parse(reader); codeParser.parse(reader);
} }
} }

View File

@ -0,0 +1,20 @@
/*
* Copyright 2024 konsoletyper.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
public interface FunctionSectionListener {
void function(int index, int typeIndex);
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2024 konsoletyper.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
public class FunctionSectionParser extends BaseSectionParser {
private final FunctionSectionListener listener;
public FunctionSectionParser(FunctionSectionListener listener) {
this.listener = listener;
}
@Override
protected void parseContent() {
var count = reader.readLEB();
for (var i = 0; i < count; ++i) {
reportAddress();
var typeIndex = reader.readLEB();
listener.function(i, typeIndex);
}
}
}

View File

@ -18,17 +18,12 @@ package org.teavm.backend.wasm.parser;
public class GlobalSectionParser extends BaseSectionParser { public class GlobalSectionParser extends BaseSectionParser {
private final GlobalSectionListener listener; private final GlobalSectionListener listener;
private CodeParser codeParser; private CodeParser codeParser;
private int functionIndexOffset;
public GlobalSectionParser(GlobalSectionListener listener) { public GlobalSectionParser(GlobalSectionListener listener) {
this.listener = listener; this.listener = listener;
codeParser = new CodeParser(); codeParser = new CodeParser();
} }
public void setFunctionIndexOffset(int functionIndexOffset) {
this.functionIndexOffset = functionIndexOffset;
}
@Override @Override
protected void parseContent() { protected void parseContent() {
var count = readLEB(); var count = readLEB();
@ -41,7 +36,6 @@ public class GlobalSectionParser extends BaseSectionParser {
codeListener = CodeListener.EMPTY; codeListener = CodeListener.EMPTY;
} }
codeParser.setCodeListener(codeListener); codeParser.setCodeListener(codeListener);
codeParser.setFunctionIndexOffset(functionIndexOffset);
if (!codeParser.parseSingleExpression(reader)) { if (!codeParser.parseSingleExpression(reader)) {
throw new ParseException("Error parsing global initializer", reader.ptr); throw new ParseException("Error parsing global initializer", reader.ptr);
} }

View File

@ -0,0 +1,26 @@
/*
* Copyright 2024 konsoletyper.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.parser;
public class WasmHollowFunctionType {
public final WasmHollowType[] parameterTypes;
public final WasmHollowType[] returnTypes;
public WasmHollowFunctionType(WasmHollowType[] parameterTypes, WasmHollowType[] returnTypes) {
this.parameterTypes = parameterTypes;
this.returnTypes = returnTypes;
}
}