Commit 7890a89d authored by Jakob Kummerow's avatar Jakob Kummerow Committed by V8 LUCI CQ

[wasm] Add FunctionBodyDisassembler

Unused as of this CL; users will follow.

Bug: v8:12917
Change-Id: I82658ea8a401834a5b3661068766bbdfec54d5a4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3726214Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#81533}
parent c52224fd
......@@ -2525,6 +2525,7 @@ filegroup(
"src/wasm/streaming-decoder.cc",
"src/wasm/streaming-decoder.h",
"src/wasm/string-builder.h",
"src/wasm/string-builder-multiline.h",
"src/wasm/struct-types.h",
"src/wasm/sync-streaming-decoder.cc",
"src/wasm/value-type.cc",
......@@ -2534,6 +2535,8 @@ filegroup(
"src/wasm/wasm-code-manager.h",
"src/wasm/wasm-debug.cc",
"src/wasm/wasm-debug.h",
"src/wasm/wasm-disassembler.cc",
"src/wasm/wasm-disassembler-impl.h",
"src/wasm/wasm-engine.cc",
"src/wasm/wasm-engine.h",
"src/wasm/wasm-external-refs.cc",
......
......@@ -3618,12 +3618,14 @@ v8_header_set("v8_internal_headers") {
"src/wasm/simd-shuffle.h",
"src/wasm/stacks.h",
"src/wasm/streaming-decoder.h",
"src/wasm/string-builder-multiline.h",
"src/wasm/string-builder.h",
"src/wasm/struct-types.h",
"src/wasm/value-type.h",
"src/wasm/wasm-arguments.h",
"src/wasm/wasm-code-manager.h",
"src/wasm/wasm-debug.h",
"src/wasm/wasm-disassembler-impl.h",
"src/wasm/wasm-engine.h",
"src/wasm/wasm-external-refs.h",
"src/wasm/wasm-feature-flags.h",
......@@ -4707,6 +4709,7 @@ v8_source_set("v8_base_without_compiler") {
"src/wasm/value-type.cc",
"src/wasm/wasm-code-manager.cc",
"src/wasm/wasm-debug.cc",
"src/wasm/wasm-disassembler.cc",
"src/wasm/wasm-engine.cc",
"src/wasm/wasm-external-refs.cc",
"src/wasm/wasm-features.cc",
......
This diff is collapsed.
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif // !V8_ENABLE_WEBASSEMBLY
#ifndef V8_WASM_STRING_BUILDER_MULTILINE_H_
#define V8_WASM_STRING_BUILDER_MULTILINE_H_
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
#include "src/wasm/string-builder.h"
namespace v8 {
namespace internal {
namespace wasm {
struct LabelInfo {
LabelInfo(size_t line_number, size_t offset,
uint32_t index_by_occurrence_order)
: name_section_index(index_by_occurrence_order),
line_number(line_number),
offset(offset) {}
uint32_t name_section_index;
size_t line_number;
size_t offset;
const char* start{nullptr};
size_t length{0};
};
class MultiLineStringBuilder : public StringBuilder {
public:
MultiLineStringBuilder() : StringBuilder(kKeepOldChunks) {}
void NextLine(uint32_t byte_offset) {
*allocate(1) = '\n';
size_t len = length();
lines_.emplace_back(start(), len, pending_bytecode_offset_);
start_here();
pending_bytecode_offset_ = byte_offset;
}
size_t line_number() { return lines_.size(); }
void set_current_line_bytecode_offset(uint32_t offset) {
pending_bytecode_offset_ = offset;
}
// Label backpatching support. Parameters:
// {label}: Information about where to insert the label. Fields {line_number},
// {offset}, and {length} must already be populated; {start} will be populated
// with the location where the inserted label was written in memory. Note that
// this will become stale/invalid if the same line is patched again!
// {label_source}: Pointer to the characters forming the snippet that is to
// be inserted into the position described by {label}. The length of this
// snippet is passed in {label.length}.
void PatchLabel(LabelInfo& label, const char* label_source) {
DCHECK_GT(label.length, 0);
DCHECK_LT(label.line_number, lines_.size());
// Step 1: Patching a line makes it longer, and we can't grow it in-place
// because it's boxed in, so allocate space for its patched copy.
char* patched_line;
Line& l = lines_[label.line_number];
// +1 because we add a space before the label: "block" -> "block $label0",
// "block i32" -> "block $label0 i32".
size_t patched_length = l.len + label.length + 1;
if (length() == 0) {
// No current unfinished line. Allocate the patched line as if it was
// the next line.
patched_line = allocate(patched_length);
start_here();
} else {
// Shift the current unfinished line out of the way.
// TODO(jkummerow): This approach ends up being O(n²) for a `br_table`
// with `n` labels. If that ever becomes a problem, we could allocate a
// separate new chunk for patched copies of old lines, then we wouldn't
// need to shift the unfinished line around.
const char* unfinished_start = start(); // Remember the unfinished
size_t unfinished_length = length(); // line, and...
rewind_to_start(); // ...free up its space.
patched_line = allocate(patched_length);
// Write the unfinished line into its new location.
start_here();
char* new_location = allocate(unfinished_length);
memcpy(new_location, unfinished_start, unfinished_length);
if (label_source >= unfinished_start &&
label_source < unfinished_start + unfinished_length) {
label_source = new_location + (label_source - unfinished_start);
}
}
// Step 2: Write the patched copy of the line to be patched.
char* cursor = patched_line;
memcpy(cursor, l.data, label.offset);
cursor += label.offset;
*(cursor++) = ' ';
label.start = cursor;
memcpy(cursor, label_source, label.length);
cursor += label.length;
memcpy(cursor, l.data + label.offset, l.len - label.offset);
l.data = patched_line;
l.len = patched_length;
}
void DumpToStdout() {
if (length() != 0) NextLine(0);
// In the name of speed, batch up lines that happen to be stored
// consecutively.
if (lines_.size() == 0) return;
const Line& first = lines_[0];
const char* last_start = first.data;
size_t len = first.len;
for (size_t i = 1; i < lines_.size(); i++) {
const Line& l = lines_[i];
if (last_start + len == l.data) {
len += l.len;
} else {
std::cout.write(last_start, len);
last_start = l.data;
len = l.len;
}
}
std::cout.write(last_start, len);
}
private:
struct Line {
Line(const char* d, size_t length, uint32_t bytecode_offset)
: data(d), len(length), bytecode_offset(bytecode_offset) {}
const char* data;
size_t len;
uint32_t bytecode_offset;
};
std::vector<Line> lines_;
uint32_t pending_bytecode_offset_ = 0;
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_STRING_BUILDER_MULTILINE_H_
......@@ -25,7 +25,7 @@ namespace wasm {
// MultiLineStringBuilder.
class StringBuilder {
public:
explicit StringBuilder() : on_growth_(kReplacePreviousChunk) {}
StringBuilder() : on_growth_(kReplacePreviousChunk) {}
explicit StringBuilder(const StringBuilder&) = delete;
StringBuilder& operator=(const StringBuilder&) = delete;
~StringBuilder() {
......@@ -35,7 +35,7 @@ class StringBuilder {
// Reserves space for {n} characters and returns a pointer to its beginning.
// Clients *must* write all {n} characters after calling this!
// Don't call this directly, use operator<< overloads instead.
char* write(size_t n) {
char* allocate(size_t n) {
if (remaining_bytes_ < n) Grow();
char* result = cursor_;
cursor_ += n;
......@@ -44,11 +44,11 @@ class StringBuilder {
}
// Convenience wrappers.
void write(const byte* data, size_t n) {
char* ptr = write(n);
char* ptr = allocate(n);
memcpy(ptr, data, n);
}
void write(const char* data, size_t n) {
char* ptr = write(n);
char* ptr = allocate(n);
memcpy(ptr, data, n);
}
......@@ -97,13 +97,13 @@ class StringBuilder {
inline StringBuilder& operator<<(StringBuilder& sb, const char* str) {
size_t len = strlen(str);
char* ptr = sb.write(len);
char* ptr = sb.allocate(len);
memcpy(ptr, str, len);
return sb;
}
inline StringBuilder& operator<<(StringBuilder& sb, char c) {
*sb.write(1) = c;
*sb.allocate(1) = c;
return sb;
}
......@@ -114,7 +114,7 @@ inline StringBuilder& operator<<(StringBuilder& sb, const std::string& s) {
inline StringBuilder& operator<<(StringBuilder& sb, uint32_t n) {
if (n == 0) {
*sb.write(1) = '0';
*sb.allocate(1) = '0';
return sb;
}
static constexpr size_t kBufferSize = 10; // Just enough for a uint32.
......
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif // !V8_ENABLE_WEBASSEMBLY
#ifndef V8_WASM_WASM_DISASSEMBLER_IMPL_H_
#define V8_WASM_WASM_DISASSEMBLER_IMPL_H_
#include <iomanip>
#include "src/wasm/function-body-decoder-impl.h"
#include "src/wasm/names-provider.h"
#include "src/wasm/string-builder-multiline.h"
#include "src/wasm/wasm-opcodes.h"
namespace v8 {
namespace internal {
namespace wasm {
template <Decoder::ValidateFlag validate>
class ImmediatesPrinter;
using IndexAsComment = NamesProvider::IndexAsComment;
////////////////////////////////////////////////////////////////////////////////
// Configuration flags for aspects of behavior where we might want to change
// our minds. {true} is the legacy DevTools behavior.
constexpr IndexAsComment kIndicesAsComments = NamesProvider::kIndexAsComment;
constexpr bool kSkipDataSegmentNames = true;
////////////////////////////////////////////////////////////////////////////////
// Helpers.
class Indentation {
public:
Indentation(int current, int delta) : current_(current), delta_(delta) {
DCHECK_GE(current, 0);
DCHECK_GE(delta, 0);
}
Indentation Extra(int extra) { return {current_ + extra, delta_}; }
void increase() { current_ += delta_; }
void decrease() {
DCHECK_GE(current_, delta_);
current_ -= delta_;
}
int current() { return current_; }
private:
int current_;
int delta_;
};
inline StringBuilder& operator<<(StringBuilder& sb, Indentation indentation) {
char* ptr = sb.allocate(indentation.current());
memset(ptr, ' ', indentation.current());
return sb;
}
void PrintSignatureOneLine(
StringBuilder& out, const FunctionSig* sig, uint32_t func_index,
NamesProvider* names, bool param_names,
IndexAsComment indices_as_comments = NamesProvider::kDontPrintIndex);
////////////////////////////////////////////////////////////////////////////////
// FunctionBodyDisassembler.
class FunctionBodyDisassembler : public WasmDecoder<Decoder::kFullValidation> {
public:
static constexpr Decoder::ValidateFlag validate = Decoder::kFullValidation;
enum FunctionHeader : bool { kSkipHeader = false, kPrintHeader = true };
FunctionBodyDisassembler(Zone* zone, const WasmModule* module,
uint32_t func_index, WasmFeatures* detected,
const FunctionSig* sig, const byte* start,
const byte* end, uint32_t offset,
NamesProvider* names)
: WasmDecoder<validate>(zone, module, WasmFeatures::All(), detected, sig,
start, end, offset),
func_index_(func_index),
names_(names) {}
void DecodeAsWat(MultiLineStringBuilder& out, Indentation indentation,
FunctionHeader include_header = kPrintHeader);
void DecodeGlobalInitializer(StringBuilder& out);
std::set<uint32_t>& used_types() { return used_types_; }
protected:
WasmOpcode GetOpcode();
uint32_t PrintImmediatesAndGetLength(StringBuilder& out);
void PrintHexNumber(StringBuilder& out, uint64_t number);
LabelInfo& label_info(int depth) {
return label_stack_[label_stack_.size() - 1 - depth];
}
friend class ImmediatesPrinter<validate>;
uint32_t func_index_;
WasmOpcode current_opcode_ = kExprUnreachable;
NamesProvider* names_;
std::set<uint32_t> used_types_;
std::vector<LabelInfo> label_stack_;
MultiLineStringBuilder* out_;
// Labels use two different indexing systems: for looking them up in the
// name section, they're indexed by order of occurrence; for generating names
// like "$label0", the order in which they show up as targets of branch
// instructions is used for generating consecutive names.
// (This is legacy wasmparser behavior; we could change it.)
uint32_t label_occurrence_index_ = 0;
uint32_t label_generation_index_ = 0;
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_WASM_DISASSEMBLER_IMPL_H_
This diff is collapsed.
......@@ -176,8 +176,8 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(I32Shl, 0x74, i_ii, "i32.shl") \
V(I32ShrS, 0x75, i_ii, "i32.shr_s") \
V(I32ShrU, 0x76, i_ii, "i32.shr_u") \
V(I32Rol, 0x77, i_ii, "i32.rol") \
V(I32Ror, 0x78, i_ii, "i32.ror") \
V(I32Rol, 0x77, i_ii, "i32.rotl") \
V(I32Ror, 0x78, i_ii, "i32.rotr") \
V(I64Clz, 0x79, l_l, "i64.clz") \
V(I64Ctz, 0x7a, l_l, "i64.ctz") \
V(I64Popcnt, 0x7b, l_l, "i64.popcnt") \
......@@ -191,8 +191,8 @@ bool V8_EXPORT_PRIVATE IsJSCompatibleSignature(const FunctionSig* sig,
V(I64Shl, 0x86, l_ll, "i64.shl") \
V(I64ShrS, 0x87, l_ll, "i64.shr_s") \
V(I64ShrU, 0x88, l_ll, "i64.shr_u") \
V(I64Rol, 0x89, l_ll, "i64.rol") \
V(I64Ror, 0x8a, l_ll, "i64.ror") \
V(I64Rol, 0x89, l_ll, "i64.rotl") \
V(I64Ror, 0x8a, l_ll, "i64.rotr") \
V(F32Abs, 0x8b, f_f, "f32.abs") \
V(F32Neg, 0x8c, f_f, "f32.neg") \
V(F32Ceil, 0x8d, f_f, "f32.ceil") \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment