blob: 90bc500968991391f9be89a01ce5c6d0931723e2 [file] [log] [blame]
// 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.
#ifndef V8_WASM_STRING_BUILDER_MULTILINE_H_
#define V8_WASM_STRING_BUILDER_MULTILINE_H_
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif // !V8_ENABLE_WEBASSEMBLY
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
#include "src/wasm/string-builder.h"
namespace v8 {
namespace debug {
class DisassemblyCollector;
} // namespace debug
namespace internal {
namespace wasm {
// Computes the number of decimal digits required to print {value}.
inline int GetNumDigits(uint32_t value) {
int digits = 1;
for (uint32_t compare = 10; value >= compare; compare *= 10) digits++;
return digits;
}
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;
}
uint32_t current_line_bytecode_offset() { return pending_bytecode_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);
memmove(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;
}
// Note: implemented in wasm-disassembler.cc (which is also the only user).
void ToDisassemblyCollector(v8::debug::DisassemblyCollector* collector);
void WriteTo(std::ostream& out, bool print_offsets,
std::vector<uint32_t>* collect_offsets = nullptr) {
if (length() != 0) NextLine(0);
if (lines_.size() == 0) return;
if (print_offsets) {
// The last offset is expected to be the largest.
int width = GetNumDigits(lines_.back().bytecode_offset);
// We could have used std::setw(width), but this is faster.
constexpr int kBufSize = 12; // Enough for any uint32 plus '|'.
char buffer[kBufSize] = {32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, '|'};
char* const buffer_end = buffer + kBufSize - 1;
char* const buffer_start = buffer_end - width;
for (const Line& l : lines_) {
uint32_t offset = l.bytecode_offset;
char* ptr = buffer_end;
do {
*(--ptr) = '0' + (offset % 10);
offset /= 10;
// We pre-filled the buffer with spaces, and the offsets are expected
// to be increasing, so we can just stop the loop here and don't need
// to write spaces until {ptr == buffer_start}.
} while (offset > 0);
out.write(buffer_start, width + 1); // +1 for the '|'.
out.write(l.data, l.len);
}
return;
}
// In the name of speed, batch up lines that happen to be stored
// consecutively.
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 {
out.write(last_start, len);
last_start = l.data;
len = l.len;
}
}
out.write(last_start, len);
if (collect_offsets) {
collect_offsets->reserve(lines_.size());
for (const Line& l : lines_) {
collect_offsets->push_back(l.bytecode_offset);
}
}
}
size_t ApproximateSizeMB() { return approximate_size_mb(); }
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_