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

[wasm] Process feedback for multi-level call_direct inlining

For call_direct, feedback (for the first level of inlining) only
records call counts, not the statically-known target. So to be
able to make feedback for potential additional inlining levels
available, the feedback processor must look to the wire bytes
to extract the call targets. Without feedback and hence unknown
call counts, such multi-level inlining would not happen.

Bug: v8:12166
Change-Id: I84ca58019e927a8bf9dad4e4aceddd341f945c04
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3579105Reviewed-by: 's avatarManos Koukoutos <manoskouk@chromium.org>
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#80139}
parent 2e3fb275
......@@ -1260,13 +1260,14 @@ class TransitiveTypeFeedbackProcessor {
}
private:
static constexpr uint32_t kNonDirectCall = 0xFFFFFFFF;
void Process(int func_index);
std::vector<uint32_t> GetCallDirectTargets(int func_index);
void EnqueueCallees(std::vector<CallSiteFeedback> feedback) {
for (size_t i = 0; i < feedback.size(); i++) {
int func = feedback[i].function_index;
// TODO(jkummerow): Find a way to get the target function ID for
// direct calls (which currently requires decoding the function).
// Nothing to do for non-inlineable (e.g. megamorphic) calls.
if (func == -1) continue;
// Don't spend time on calls that have never been executed.
if (feedback[i].absolute_call_frequency == 0) continue;
......@@ -1285,6 +1286,44 @@ class TransitiveTypeFeedbackProcessor {
std::unordered_set<int> queue_;
};
// For every recorded position of a call instruction in the given function,
// extracts the bytes that constitute the target function index if the call
// is a call_direct. We don't know the type of call, so this will produce
// bogus bytes for other types of calls.
std::vector<uint32_t> TransitiveTypeFeedbackProcessor::GetCallDirectTargets(
int func_index) {
WasmModuleObject module_object = instance_->module_object();
const NativeModule* native_module = module_object.native_module();
base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
const WasmModule* module = native_module->module();
const WasmFunction* func = &module->functions[func_index];
const byte* func_start = wire_bytes.begin() + func->code.offset();
Decoder decoder(wire_bytes);
std::map<WasmCodePosition, int> positions =
module->type_feedback.feedback_for_function[func_index].positions;
size_t num_calls = positions.size();
std::vector<uint32_t> result(num_calls);
for (auto entry : positions) {
int position = entry.first;
int call_index = entry.second;
const byte* pc = func_start + position;
uint8_t call = decoder.read_u8<Decoder::kNoValidation>(pc);
if (call != kExprCallFunction) {
result[call_index] = kNonDirectCall;
continue;
}
static constexpr int kCallDirectInstructionLength = 1;
pc += kCallDirectInstructionLength;
uint32_t length_dummy;
uint32_t immediate =
decoder.read_u32v<Decoder::kNoValidation>(pc, &length_dummy);
result[call_index] = immediate;
}
return result;
}
void TransitiveTypeFeedbackProcessor::Process(int func_index) {
int which_vector = declared_function_index(instance_->module(), func_index);
Object maybe_feedback = instance_->feedback_vectors().get(which_vector);
......@@ -1293,6 +1332,7 @@ void TransitiveTypeFeedbackProcessor::Process(int func_index) {
std::vector<CallSiteFeedback> result(feedback.length() / 2);
int imported_functions =
static_cast<int>(instance_->module()->num_imported_functions);
std::vector<uint32_t> call_direct_targets = GetCallDirectTargets(func_index);
for (int i = 0; i < feedback.length(); i += 2) {
Object value = feedback.get(i);
if (value.IsWasmInternalFunction() &&
......@@ -1365,21 +1405,23 @@ void TransitiveTypeFeedbackProcessor::Process(int func_index) {
i / 2, best_frequency);
}
} else if (value.IsSmi()) {
// Direct call, just collecting call count.
int count = Smi::cast(value).value();
if (FLAG_trace_wasm_speculative_inlining) {
PrintF("[Function #%d call_direct #%d: frequency %d]\n", func_index,
i / 2, count);
// Uninitialized, or a direct call collecting call count.
uint32_t target = call_direct_targets[i / 2];
if (target != kNonDirectCall) {
int count = Smi::cast(value).value();
if (FLAG_trace_wasm_speculative_inlining) {
PrintF("[Function #%d call_direct #%d: frequency %d]\n", func_index,
i / 2, count);
}
result[i / 2] = {static_cast<int>(target), count};
continue;
}
result[i / 2] = {-1, count};
continue;
}
// If we fall through to here, then this call isn't eligible for inlining.
// Possible reasons: uninitialized or megamorphic feedback; or monomorphic
// or polymorphic that didn't meet our requirements.
if (FLAG_trace_wasm_speculative_inlining) {
PrintF("[Function #%d call_ref #%d *not* inlineable]\n", func_index,
i / 2);
PrintF("[Function #%d call #%d *not* inlineable]\n", func_index, i / 2);
}
result[i / 2] = {-1, -1};
}
......
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