v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
string-builder-multiline.h
Go to the documentation of this file.
1// Copyright 2022 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_WASM_STRING_BUILDER_MULTILINE_H_
6#define V8_WASM_STRING_BUILDER_MULTILINE_H_
7
8#if !V8_ENABLE_WEBASSEMBLY
9#error This header should only be included if WebAssembly is enabled.
10#endif // !V8_ENABLE_WEBASSEMBLY
11
12#include <cstring>
13#include <iostream>
14#include <string>
15#include <vector>
16
18
19namespace v8 {
20
21namespace debug {
22class DisassemblyCollector;
23} // namespace debug
24
25namespace internal {
26namespace wasm {
27
28// Computes the number of decimal digits required to print {value}.
29inline int GetNumDigits(uint32_t value) {
30 int digits = 1;
31 for (uint32_t compare = 10; value >= compare; compare *= 10) digits++;
32 return digits;
33}
34
35struct LabelInfo {
37 uint32_t index_by_occurrence_order)
38 : name_section_index(index_by_occurrence_order),
40 offset(offset) {}
43 size_t offset;
44 const char* start{nullptr};
45 size_t length{0};
46};
47
49 public:
51
52 void NextLine(uint32_t byte_offset) {
53 *allocate(1) = '\n';
54 size_t len = length();
55 lines_.emplace_back(start(), len, pending_bytecode_offset_);
56 start_here();
57 pending_bytecode_offset_ = byte_offset;
58 }
59 size_t line_number() { return lines_.size(); }
60
65
66 // Label backpatching support. Parameters:
67 // {label}: Information about where to insert the label. Fields {line_number},
68 // {offset}, and {length} must already be populated; {start} will be populated
69 // with the location where the inserted label was written in memory. Note that
70 // this will become stale/invalid if the same line is patched again!
71 // {label_source}: Pointer to the characters forming the snippet that is to
72 // be inserted into the position described by {label}. The length of this
73 // snippet is passed in {label.length}.
74 void PatchLabel(LabelInfo& label, const char* label_source) {
75 DCHECK_GT(label.length, 0);
76 DCHECK_LT(label.line_number, lines_.size());
77
78 // Step 1: Patching a line makes it longer, and we can't grow it in-place
79 // because it's boxed in, so allocate space for its patched copy.
80 char* patched_line;
81 Line& l = lines_[label.line_number];
82 // +1 because we add a space before the label: "block" -> "block $label0",
83 // "block i32" -> "block $label0 i32".
84 size_t patched_length = l.len + label.length + 1;
85 if (length() == 0) {
86 // No current unfinished line. Allocate the patched line as if it was
87 // the next line.
88 patched_line = allocate(patched_length);
89 start_here();
90 } else {
91 // Shift the current unfinished line out of the way.
92 // TODO(jkummerow): This approach ends up being O(n²) for a `br_table`
93 // with `n` labels. If that ever becomes a problem, we could allocate a
94 // separate new chunk for patched copies of old lines, then we wouldn't
95 // need to shift the unfinished line around.
96 const char* unfinished_start = start(); // Remember the unfinished
97 size_t unfinished_length = length(); // line, and...
98 rewind_to_start(); // ...free up its space.
99 patched_line = allocate(patched_length);
100 // Write the unfinished line into its new location.
101 start_here();
102 char* new_location = allocate(unfinished_length);
103 memmove(new_location, unfinished_start, unfinished_length);
104 if (label_source >= unfinished_start &&
105 label_source < unfinished_start + unfinished_length) {
106 label_source = new_location + (label_source - unfinished_start);
107 }
108 }
109
110 // Step 2: Write the patched copy of the line to be patched.
111 char* cursor = patched_line;
112 memcpy(cursor, l.data, label.offset);
113 cursor += label.offset;
114 *(cursor++) = ' ';
115 label.start = cursor;
116 memcpy(cursor, label_source, label.length);
117 cursor += label.length;
118 memcpy(cursor, l.data + label.offset, l.len - label.offset);
119 l.data = patched_line;
120 l.len = patched_length;
121 }
122
123 // Note: implemented in wasm-disassembler.cc (which is also the only user).
125
126 void WriteTo(std::ostream& out, bool print_offsets,
127 std::vector<uint32_t>* collect_offsets = nullptr) {
128 if (length() != 0) NextLine(0);
129 if (lines_.size() == 0) return;
130
131 if (print_offsets) {
132 // The last offset is expected to be the largest.
133 int width = GetNumDigits(lines_.back().bytecode_offset);
134 // We could have used std::setw(width), but this is faster.
135 constexpr int kBufSize = 12; // Enough for any uint32 plus '|'.
136 char buffer[kBufSize] = {32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, '|'};
137 char* const buffer_end = buffer + kBufSize - 1;
138 char* const buffer_start = buffer_end - width;
139 for (const Line& l : lines_) {
140 uint32_t offset = l.bytecode_offset;
141 char* ptr = buffer_end;
142 do {
143 *(--ptr) = '0' + (offset % 10);
144 offset /= 10;
145 // We pre-filled the buffer with spaces, and the offsets are expected
146 // to be increasing, so we can just stop the loop here and don't need
147 // to write spaces until {ptr == buffer_start}.
148 } while (offset > 0);
149 out.write(buffer_start, width + 1); // +1 for the '|'.
150 out.write(l.data, l.len);
151 }
152 return;
153 }
154 // In the name of speed, batch up lines that happen to be stored
155 // consecutively.
156 const Line& first = lines_[0];
157 const char* last_start = first.data;
158 size_t len = first.len;
159 for (size_t i = 1; i < lines_.size(); i++) {
160 const Line& l = lines_[i];
161 if (last_start + len == l.data) {
162 len += l.len;
163 } else {
164 out.write(last_start, len);
165 last_start = l.data;
166 len = l.len;
167 }
168 }
169 out.write(last_start, len);
170 if (collect_offsets) {
171 collect_offsets->reserve(lines_.size());
172 for (const Line& l : lines_) {
173 collect_offsets->push_back(l.bytecode_offset);
174 }
175 }
176 }
177
179
180 private:
181 struct Line {
182 Line(const char* d, size_t length, uint32_t bytecode_offset)
183 : data(d), len(length), bytecode_offset(bytecode_offset) {}
184 const char* data;
185 size_t len;
187 };
188
189 std::vector<Line> lines_;
191};
192
193} // namespace wasm
194} // namespace internal
195} // namespace v8
196
197#endif // V8_WASM_STRING_BUILDER_MULTILINE_H_
void WriteTo(std::ostream &out, bool print_offsets, std::vector< uint32_t > *collect_offsets=nullptr)
void ToDisassemblyCollector(v8::debug::DisassemblyCollector *collector)
void PatchLabel(LabelInfo &label, const char *label_source)
Label label
int32_t offset
int GetNumDigits(uint32_t value)
Definition c-api.cc:87
uint32_t compare
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_GT(v1, v2)
Definition logging.h:487
LabelInfo(size_t line_number, size_t offset, uint32_t index_by_occurrence_order)
Line(const char *d, size_t length, uint32_t bytecode_offset)