From f06ba832d7d4700c393417202a329712acfc27c4 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 19 Nov 2022 11:52:43 +0100 Subject: [PATCH] Wasm: generate DWARF line numbers Despite generated DWARF sections pass different verifications, they don't work in chrome or wasmtime. --- .../org/teavm/backend/wasm/WasmTarget.java | 2 +- .../backend/wasm/dwarf/DwarfConstants.java | 29 +++ .../backend/wasm/dwarf/DwarfInfoWriter.java | 9 + .../teavm/backend/wasm/dwarf/blob/Blob.java | 24 +- .../backend/wasm/dwarf/blob/BlobReader.java | 14 +- .../teavm/backend/wasm/dwarf/blob/Marker.java | 4 + .../backend/wasm/generate/DwarfGenerator.java | 51 ++++- .../wasm/generate/DwarfLinesGenerator.java | 207 ++++++++++++++++++ .../backend/wasm/generate/DwarfStrings.java | 38 ++++ .../wasm/render/WasmBinaryRenderer.java | 28 ++- .../render/WasmBinaryRenderingVisitor.java | 43 +++- 11 files changed, 425 insertions(+), 24 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generate/DwarfStrings.java diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index dca54484d..0a85d5c71 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -545,7 +545,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } var writer = new WasmBinaryWriter(); - var renderer = new WasmBinaryRenderer(writer, version, obfuscated); + var renderer = new WasmBinaryRenderer(writer, version, obfuscated, dwarfGenerator); renderer.render(module, buildDwarf(dwarfGenerator)); try (OutputStream output = buildTarget.createResource(outputName)) { diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfConstants.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfConstants.java index a9b006aa4..cf15571ba 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfConstants.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfConstants.java @@ -22,9 +22,38 @@ public final class DwarfConstants { public static final int DW_TAG_COMPILE_UNIT = 0x11; + public static final int DW_AT_NAME = 0x03; + public static final int DW_AT_STMT_LIST = 0x10; + public static final int DW_AT_LOW_PC = 0x11; + public static final int DW_AT_HIGH_PC = 0x12; + public static final int DW_AT_PRODUCER = 0x25; + public static final int DW_CHILDREN_YES = 1; public static final int DW_CHILDREN_NO = 0; + public static final int DW_LNCT_PATH = 0x1; + public static final int DW_LNCT_DIRECTORY_INDEX = 0x2; + + public static final int DW_FORM_ADDR = 0x01; + public static final int DW_FORM_DATA2 = 0x05; + public static final int DW_FORM_DATA4 = 0x06; + public static final int DW_FORM_STRP = 0x0E; + public static final int DW_FORM_SEC_OFFSET = 0x17; + public static final int DW_FORM_LINE_STRP = 0x1F; + + public static final int DW_LNS_COPY = 0x01; + public static final int DW_LNS_ADVANCE_PC = 0x02; + public static final int DW_LNS_ADVANCE_LINE = 0x03; + public static final int DW_LNS_SET_FILE = 0x04; + public static final int DW_LNS_SET_COLUMN = 0x05; + public static final int DW_LNS_NEGATE_STMT = 0x06; + public static final int DW_LNS_SET_BASIC_BLOCK = 0x07; + public static final int DW_LNS_CONST_ADD_PC = 0x08; + public static final int DW_LNS_FIXED_ADVANCE_PC = 0x09; + public static final int DW_LNS_SET_PROLOGUE_END = 0x0A; + public static final int DW_LNS_SET_EPILOGUE_BEGIN = 0x0B; + public static final int DW_LNS_SET_ISA = 0x0C; + private DwarfConstants() { } } diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfInfoWriter.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfInfoWriter.java index 61038cdd2..fdec3a46d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfInfoWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/DwarfInfoWriter.java @@ -61,6 +61,15 @@ public class DwarfInfoWriter { return this; } + public DwarfInfoWriter skip(int count) { + output.skip(count); + return this; + } + + public Marker marker() { + return output.marker(); + } + public DwarfAbbreviation abbreviation(int tag, boolean hasChildren, Consumer blob) { var abbr = new DwarfAbbreviation(tag, hasChildren, blob); abbreviations.add(abbr); diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Blob.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Blob.java index f20921c9a..5f70c8164 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Blob.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Blob.java @@ -100,12 +100,30 @@ public class Blob { var buffer = this.buffer; while ((value & 0x7F) != value) { buffer[ptr++] = (byte) ((value & 0x7F) | 0x80); - value >>= 7; + value >>>= 7; } buffer[ptr++] = (byte) (value & 0x7F); return write(buffer, 0, ptr); } + public Blob writeSLEB(int value) { + var ptr = 0; + var buffer = this.buffer; + var sign = value >>> 31; + while (true) { + var digit = value & 0x7F; + value >>= 7; + var more = value != 0 && value != -1 || digit >> 6 != sign; + if (more) { + buffer[ptr++] = (byte) (digit | 0x80); + } else { + buffer[ptr++] = (byte) digit; + break; + } + } + return write(buffer, 0, ptr); + } + private void nextChunkIfNeeded() { if (posInChunk < currentChunk.length) { return; @@ -127,6 +145,10 @@ public class Blob { return new BlobReader(this, consumer); } + public BinaryDataConsumer writer() { + return this::write; + } + public Marker marker() { return new Marker(this, chunkIndex, posInChunk, ptr); } diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BlobReader.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BlobReader.java index 7ce3d255c..6d4eaaff4 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BlobReader.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/BlobReader.java @@ -16,14 +16,14 @@ package org.teavm.backend.wasm.dwarf.blob; public class BlobReader { - private Blob output; + private Blob blob; private BinaryDataConsumer consumer; private int ptr; private int currentChunk; private int offsetInChunk; - BlobReader(Blob output, BinaryDataConsumer consumer) { - this.output = output; + BlobReader(Blob blob, BinaryDataConsumer consumer) { + this.blob = blob; this.consumer = consumer; } @@ -31,8 +31,12 @@ public class BlobReader { return ptr; } + public void readRemaining() { + advance(blob.size()); + } + public void advance(int to) { - if (to < ptr || to > output.size()) { + if (to < ptr || to > blob.size()) { throw new IllegalArgumentException(); } if (to == ptr) { @@ -43,7 +47,7 @@ public class BlobReader { var currentChunk = this.currentChunk; var offsetInChunk = this.offsetInChunk; while (ptr < to) { - var chunk = output.chunkAt(currentChunk); + var chunk = blob.chunkAt(currentChunk); var limit = Math.min(ptr + chunk.length, to); var bytesToWrite = limit - ptr; consumer.accept(chunk, offsetInChunk, offsetInChunk + bytesToWrite); diff --git a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Marker.java b/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Marker.java index 1e331eef7..2e42a4027 100644 --- a/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Marker.java +++ b/core/src/main/java/org/teavm/backend/wasm/dwarf/blob/Marker.java @@ -40,4 +40,8 @@ public class Marker { posInChunk = blob.posInChunk; ptr = blob.ptr; } + + public int ptr() { + return ptr; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java index ed6acf86a..295f7e145 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfGenerator.java @@ -18,19 +18,26 @@ package org.teavm.backend.wasm.generate; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DWARF_VERSION; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_TAG_COMPILE_UNIT; import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_UT_COMPILE; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; +import org.teavm.backend.wasm.dwarf.DwarfConstants; import org.teavm.backend.wasm.dwarf.DwarfInfoWriter; import org.teavm.backend.wasm.dwarf.DwarfPlaceholder; import org.teavm.backend.wasm.dwarf.blob.Blob; +import org.teavm.backend.wasm.dwarf.blob.Marker; import org.teavm.backend.wasm.model.WasmCustomSection; public class DwarfGenerator { private DwarfInfoWriter infoWriter = new DwarfInfoWriter(); private DwarfPlaceholder endOfSection; + private DwarfStrings strings = new DwarfStrings(); + private DwarfStrings lineStrings = new DwarfStrings(); + private DwarfLinesGenerator lines = new DwarfLinesGenerator(lineStrings); + private Marker highPcMarker; public void begin() { endOfSection = infoWriter.placeholder(4); + lines.begin(); emitUnitHeader(); compilationUnit(); } @@ -56,28 +63,60 @@ public class DwarfGenerator { } private void compilationUnit() { - infoWriter.tag(infoWriter.abbreviation(DW_TAG_COMPILE_UNIT, true, data -> { })); + infoWriter.tag(infoWriter.abbreviation(DW_TAG_COMPILE_UNIT, true, data -> { + data.writeLEB(DwarfConstants.DW_AT_PRODUCER).writeLEB(DwarfConstants.DW_FORM_STRP); + data.writeLEB(DwarfConstants.DW_AT_NAME).writeLEB(DwarfConstants.DW_FORM_STRP); + data.writeLEB(DwarfConstants.DW_AT_STMT_LIST).writeLEB(DwarfConstants.DW_FORM_SEC_OFFSET); + data.writeLEB(DwarfConstants.DW_AT_LOW_PC).writeLEB(DwarfConstants.DW_FORM_ADDR); + data.writeLEB(DwarfConstants.DW_AT_HIGH_PC).writeLEB(DwarfConstants.DW_FORM_ADDR); + })); + infoWriter.writeInt(strings.stringRef("TeaVM")); + infoWriter.writeInt(strings.stringRef("classes.wasm")); + infoWriter.writeInt(0); + infoWriter.writeInt(0); + highPcMarker = infoWriter.marker(); + infoWriter.skip(4); } public void end() { closeTag(); // compilation unit infoWriter.mark(endOfSection); + lines.end(); } private void closeTag() { infoWriter.writeByte(0); } + public void setCodeSize(int codeSize) { + highPcMarker.rewind(); + infoWriter.writeInt(codeSize); + } + public Collection createSections() { + var sections = new ArrayList(); + var abbreviations = new Blob(); infoWriter.buildAbbreviations(abbreviations); + sections.add(new WasmCustomSection(".debug_abbrev", abbreviations.toArray())); var info = new Blob(); infoWriter.build(info); + sections.add(new WasmCustomSection(".debug_info", info.toArray())); - return Arrays.asList( - new WasmCustomSection(".debug_abbrev", abbreviations.toArray()), - new WasmCustomSection(".debug_info", info.toArray()) - ); + if (strings.blob.size() > 0) { + sections.add(new WasmCustomSection(".debug_str", strings.blob.toArray())); + } + if (lineStrings.blob.size() > 0) { + sections.add(new WasmCustomSection(".debug_line_str", lineStrings.blob.toArray())); + } + + sections.add(new WasmCustomSection(".debug_line", lines.blob.toArray())); + + return sections; + } + + public void lineNumber(int address, String fileName, int lineNumber) { + lines.lineNumber(address, fileName, lineNumber); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java new file mode 100644 index 000000000..717fde85a --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfLinesGenerator.java @@ -0,0 +1,207 @@ +/* + * Copyright 2022 Alexey Andreev. + * + * 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.generate; + +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_DATA2; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_FORM_LINE_STRP; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNCT_DIRECTORY_INDEX; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNCT_PATH; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNS_ADVANCE_LINE; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNS_ADVANCE_PC; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNS_COPY; +import static org.teavm.backend.wasm.dwarf.DwarfConstants.DW_LNS_SET_FILE; +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import org.teavm.backend.wasm.dwarf.blob.Blob; +import org.teavm.backend.wasm.dwarf.blob.Marker; + +class DwarfLinesGenerator { + private static final int MIN_INSN_LEN = 1; + private static final int LINE_BASE = -3; + private static final int LINE_RANGE = 8; + private static final int OPCODE_BASE = 13; + + Blob blob = new Blob(); + private DwarfStrings strings; + private Blob instructionsBlob = new Blob(); + private Marker unitLengthMarker; + private Marker headerLengthMarker; + private Blob filesBlob = new Blob(); + private ObjectIntMap fileIndexes = new ObjectIntHashMap<>(); + private Blob dirsBlob = new Blob(); + private ObjectIntMap dirIndexes = new ObjectIntHashMap<>(); + private int address; + private int file = 1; + private int line = 1; + + DwarfLinesGenerator(DwarfStrings strings) { + this.strings = strings; + } + + void begin() { + emitLinesHeader(); + } + + private void emitLinesHeader() { + // length + unitLengthMarker = blob.marker(); + blob.skip(4); + + // version + blob.writeShort(5); + + // address_size + blob.writeByte(4); + + // segment_selector_size + blob.writeByte(0); + + // header_length + headerLengthMarker = blob.marker(); + blob.skip(4); + + // minimum_instruction_length + blob.writeByte(MIN_INSN_LEN); + + // maximum_operations_per_instruction + blob.writeByte(1); + + // default_is_stmt + blob.writeByte(1); + + blob.writeByte(LINE_BASE).writeByte(LINE_RANGE).writeByte(OPCODE_BASE); + + // standard_opcode_lengths + + blob.writeByte(0); // DW_LNS_COPY + blob.writeByte(1); // DW_LNS_ADVANCE_PC + blob.writeByte(1); // DW_LNS_ADVANCE_LINE + blob.writeByte(1); // DW_LNS_SET_FILE + blob.writeByte(1); // DW_LNS_SET_COLUMN + blob.writeByte(0); // DW_LNS_NEGATE_STMT + blob.writeByte(0); // DW_LNS_SET_BASIC_BLOCK + blob.writeByte(0); // DW_LNS_CONST_ADD_PC + blob.writeByte(1); // DW_LNS_FIXED_ADVANCE_PC + blob.writeByte(0); // DW_LNS_SET_PROLOGUE_END + blob.writeByte(0); // DW_LNS_SET_EPILOGUE_BEGIN + blob.writeByte(1); // DW_LNS_SET_ISA + } + + void end() { + emitDirsAndFiles(); + finishHeader(); + + instructionsBlob.newReader(blob.writer()).readRemaining(); + + var length = blob.ptr() - unitLengthMarker.ptr() - 4; + unitLengthMarker.rewind(); + blob.writeInt(length); + } + + private void emitDirsAndFiles() { + blob.writeByte(1); // directory_entry_format_count + blob.writeByte(DW_LNCT_PATH); + blob.writeByte(DW_FORM_LINE_STRP); + blob.writeLEB(dirIndexes.size()); + dirsBlob.newReader(blob.writer()).readRemaining(); + + blob.writeByte(2); // file_name_entry_format_count + blob.writeByte(DW_LNCT_DIRECTORY_INDEX); + blob.writeByte(DW_FORM_DATA2); + blob.writeByte(DW_LNCT_PATH); + blob.writeByte(DW_FORM_LINE_STRP); + blob.writeLEB(fileIndexes.size()); + filesBlob.newReader(blob.writer()).readRemaining(); + } + + private void finishHeader() { + var marker = blob.marker(); + var headerLength = blob.ptr() - headerLengthMarker.ptr() - 4; + headerLengthMarker.rewind(); + blob.writeInt(headerLength); + marker.rewind(); + } + + private int fileRef(String path) { + var ref = fileIndexes.getOrDefault(path, -1); + if (ref < 0) { + var nameIndex = path.lastIndexOf('/') + 1; + var name = path.substring(nameIndex); + var dir = path.substring(0, Math.max(0, nameIndex - 1)); + + var dirPtr = dirRef(dir); + ref = fileIndexes.size(); + fileIndexes.put(path, ref); + + filesBlob.writeShort(dirPtr); + filesBlob.writeInt(strings.stringRef(name)); + } + return ref; + } + + private int dirRef(String path) { + var ref = dirIndexes.getOrDefault(path, -1); + if (ref < 0) { + ref = dirIndexes.size(); + dirIndexes.put(path, ref); + dirsBlob.writeInt(strings.stringRef(path)); + } + return ref; + } + + void lineNumber(int address, String file, int line) { + var changed = false; + var fileRef = fileRef(file); + if (fileRef != this.file) { + this.file = fileRef; + instructionsBlob.writeByte(DW_LNS_SET_FILE); + instructionsBlob.writeLEB(fileRef); + changed = true; + } + if (address != this.address || line != this.line) { + int lineIncrement = line - this.line; + int addressIncrement = address - this.address; + if (!tryEmitSpecial(lineIncrement, addressIncrement)) { + if (lineIncrement != 0) { + instructionsBlob.writeByte(DW_LNS_ADVANCE_LINE); + instructionsBlob.writeSLEB(lineIncrement); + } + if (addressIncrement != 0) { + instructionsBlob.writeByte(DW_LNS_ADVANCE_PC); + instructionsBlob.writeSLEB(addressIncrement); + } + } + changed = true; + this.line = line; + this.address = address; + } + if (changed) { + instructionsBlob.writeByte(DW_LNS_COPY); + } + } + + private boolean tryEmitSpecial(int lineIncrement, int addressIncrement) { + if (lineIncrement < LINE_BASE || lineIncrement >= LINE_BASE + LINE_RANGE) { + return false; + } + int opcode = lineIncrement - LINE_BASE + (LINE_RANGE * addressIncrement) + OPCODE_BASE; + if (opcode <= OPCODE_BASE || opcode > 255) { + return false; + } + instructionsBlob.writeByte(opcode); + return true; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/DwarfStrings.java b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfStrings.java new file mode 100644 index 000000000..3abf78320 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/DwarfStrings.java @@ -0,0 +1,38 @@ +/* + * Copyright 2022 Alexey Andreev. + * + * 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.generate; + +import com.carrotsearch.hppc.ObjectIntHashMap; +import com.carrotsearch.hppc.ObjectIntMap; +import java.nio.charset.StandardCharsets; +import org.teavm.backend.wasm.dwarf.blob.Blob; + +class DwarfStrings { + final Blob blob = new Blob(); + private ObjectIntMap offsets = new ObjectIntHashMap<>(); + + int stringRef(String s) { + int ptr = offsets.getOrDefault(s, -1); + if (ptr < 0) { + ptr = blob.size(); + offsets.put(s, ptr); + var bytes = s.getBytes(StandardCharsets.UTF_8); + blob.write(bytes); + blob.writeByte(0); + } + return ptr; + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java index 67581660d..020e44267 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderer.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; +import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.model.WasmCustomSection; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmLocal; @@ -53,11 +54,14 @@ public class WasmBinaryRenderer { private Map signatureIndexes = new HashMap<>(); private Map functionIndexes = new HashMap<>(); private boolean obfuscated; + private DwarfGenerator dwarfGenerator; - public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated) { + public WasmBinaryRenderer(WasmBinaryWriter output, WasmBinaryVersion version, boolean obfuscated, + DwarfGenerator dwarfGenerator) { this.output = output; this.version = version; this.obfuscated = obfuscated; + this.dwarfGenerator = dwarfGenerator; } public void render(WasmModule module) { @@ -256,24 +260,28 @@ public class WasmBinaryRenderer { } private void renderCode(WasmModule module) { - WasmBinaryWriter section = new WasmBinaryWriter(); + var section = new WasmBinaryWriter(); - List functions = module.getFunctions().values().stream() + var functions = module.getFunctions().values().stream() .filter(function -> function.getImportName() == null) .collect(Collectors.toList()); section.writeLEB(functions.size()); - for (WasmFunction function : functions) { - byte[] body = renderFunction(function); + for (var function : functions) { + var body = renderFunction(function, section.getPosition()); section.writeLEB(body.length); section.writeBytes(body); } + if (dwarfGenerator != null) { + dwarfGenerator.setCodeSize(section.getPosition()); + } + writeSection(SECTION_CODE, "code", section.getData()); } - private byte[] renderFunction(WasmFunction function) { - WasmBinaryWriter code = new WasmBinaryWriter(); + private byte[] renderFunction(WasmFunction function, int offset) { + var code = new WasmBinaryWriter(); List localVariables = function.getLocalVariables(); int parameterCount = Math.min(function.getParameters().size(), localVariables.size()); @@ -301,9 +309,9 @@ public class WasmBinaryRenderer { } } - Map importIndexes = this.functionIndexes; - WasmBinaryRenderingVisitor visitor = new WasmBinaryRenderingVisitor(code, version, functionIndexes, - importIndexes, signatureIndexes); + var importIndexes = this.functionIndexes; + var visitor = new WasmBinaryRenderingVisitor(code, version, functionIndexes, importIndexes, + signatureIndexes, dwarfGenerator, offset); for (WasmExpression part : function.getBody()) { part.acceptVisitor(visitor); } diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java index 1c56a541f..ff3afc7e9 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java @@ -17,6 +17,7 @@ package org.teavm.backend.wasm.render; import java.util.HashMap; import java.util.Map; +import org.teavm.backend.wasm.generate.DwarfGenerator; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -57,16 +58,21 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { private Map functionIndexes; private Map importedIndexes; private Map signatureIndexes; + private DwarfGenerator dwarfGenerator; + private int addressOffset; private int depth; private Map blockDepths = new HashMap<>(); WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmBinaryVersion version, Map functionIndexes, - Map importedIndexes, Map signatureIndexes) { + Map importedIndexes, Map signatureIndexes, + DwarfGenerator dwarfGenerator, int addressOffset) { this.writer = writer; this.version = version; this.functionIndexes = functionIndexes; this.importedIndexes = importedIndexes; this.signatureIndexes = signatureIndexes; + this.dwarfGenerator = dwarfGenerator; + this.addressOffset = addressOffset; } @Override @@ -95,6 +101,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { } expression.getCondition().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x0D); writeLabel(expression.getTarget()); @@ -106,6 +113,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { expression.getResult().acceptVisitor(this); } + emitLocation(expression); writer.writeByte(0x0C); writeLabel(expression.getTarget()); @@ -115,6 +123,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmSwitch expression) { expression.getSelector().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x0E); writer.writeLEB(expression.getTargets().size()); @@ -132,6 +141,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmConditional expression) { expression.getCondition().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x04); writeBlockType(expression.getType()); @@ -160,40 +170,47 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { if (expression.getValue() != null) { expression.getValue().acceptVisitor(this); } + emitLocation(expression); writer.writeByte(0x0F); } @Override public void visit(WasmUnreachable expression) { + emitLocation(expression); writer.writeByte(0x0); } @Override public void visit(WasmInt32Constant expression) { + emitLocation(expression); writer.writeByte(0x41); writer.writeSignedLEB(expression.getValue()); } @Override public void visit(WasmInt64Constant expression) { + emitLocation(expression); writer.writeByte(0x42); writer.writeSignedLEB(expression.getValue()); } @Override public void visit(WasmFloat32Constant expression) { + emitLocation(expression); writer.writeByte(0x43); writer.writeFixed(Float.floatToRawIntBits(expression.getValue())); } @Override public void visit(WasmFloat64Constant expression) { + emitLocation(expression); writer.writeByte(0x44); writer.writeFixed(Double.doubleToRawLongBits(expression.getValue())); } @Override public void visit(WasmGetLocal expression) { + emitLocation(expression); writer.writeByte(0x20); writer.writeLEB(expression.getLocal().getIndex()); } @@ -201,6 +218,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmSetLocal expression) { expression.getValue().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x21); writer.writeLEB(expression.getLocal().getIndex()); } @@ -209,6 +227,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmIntBinary expression) { expression.getFirst().acceptVisitor(this); expression.getSecond().acceptVisitor(this); + emitLocation(expression); render0xD(expression); } @@ -379,6 +398,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmFloatBinary expression) { expression.getFirst().acceptVisitor(this); expression.getSecond().acceptVisitor(this); + emitLocation(expression); render0xD(expression); } @@ -503,6 +523,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmFloatUnary expression) { expression.getOperand().acceptVisitor(this); + emitLocation(expression); render0xD(expression); } @@ -570,6 +591,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmConversion expression) { expression.getOperand().acceptVisitor(this); + emitLocation(expression); switch (expression.getSourceType()) { case INT32: switch (expression.getTargetType()) { @@ -658,6 +680,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { Integer functionIndex = !expression.isImported() ? functionIndexes.get(expression.getFunctionName()) : importedIndexes.get(expression.getFunctionName()); + emitLocation(expression); if (functionIndex == null) { writer.writeByte(0x00); return; @@ -688,12 +711,14 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmDrop expression) { expression.getOperand().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x1A); } @Override public void visit(WasmLoadInt32 expression) { expression.getIndex().acceptVisitor(this); + emitLocation(expression); switch (expression.getConvertFrom()) { case INT8: writer.writeByte(0x2C); @@ -718,6 +743,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmLoadInt64 expression) { expression.getIndex().acceptVisitor(this); + emitLocation(expression); switch (expression.getConvertFrom()) { case INT8: writer.writeByte(0x30); @@ -748,6 +774,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmLoadFloat32 expression) { expression.getIndex().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x2A); writer.writeByte(alignment(expression.getAlignment())); writer.writeLEB(expression.getOffset()); @@ -756,6 +783,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmLoadFloat64 expression) { expression.getIndex().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x2B); writer.writeByte(alignment(expression.getAlignment())); writer.writeLEB(expression.getOffset()); @@ -765,6 +793,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmStoreInt32 expression) { expression.getIndex().acceptVisitor(this); expression.getValue().acceptVisitor(this); + emitLocation(expression); switch (expression.getConvertTo()) { case INT8: case UINT8: @@ -786,6 +815,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmStoreInt64 expression) { expression.getIndex().acceptVisitor(this); expression.getValue().acceptVisitor(this); + emitLocation(expression); switch (expression.getConvertTo()) { case INT8: case UINT8: @@ -811,6 +841,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmStoreFloat32 expression) { expression.getIndex().acceptVisitor(this); expression.getValue().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x38); writer.writeByte(alignment(expression.getAlignment())); writer.writeLEB(expression.getOffset()); @@ -820,6 +851,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { public void visit(WasmStoreFloat64 expression) { expression.getIndex().acceptVisitor(this); expression.getValue().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x39); writer.writeByte(alignment(expression.getAlignment())); writer.writeLEB(expression.getOffset()); @@ -828,6 +860,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { @Override public void visit(WasmMemoryGrow expression) { expression.getAmount().acceptVisitor(this); + emitLocation(expression); writer.writeByte(0x40); writer.writeByte(0); } @@ -840,4 +873,12 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { int blockDepth = blockDepths.get(target); writer.writeLEB(depth - blockDepth); } + + private void emitLocation(WasmExpression expression) { + if (dwarfGenerator == null || expression.getLocation() == null) { + return; + } + dwarfGenerator.lineNumber(writer.getPosition() + addressOffset, expression.getLocation().getFileName(), + expression.getLocation().getLine()); + } }