blob: 2cd13916e94f6a1a3bd8eaa331889ddb117dc833 [file] [log] [blame] [edit]
/*
* Copyright 2025 WebAssembly Community Group participants
*
* 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.
*/
#ifndef wasm_ir_branch_hint_h
#define wasm_ir_branch_hint_h
#include "wasm.h"
//
// Branch hint utilities to get them, set, flip, etc.
//
namespace wasm::BranchHints {
// Get the branch hint for an expression.
inline std::optional<bool> get(Expression* expr, Function* func) {
auto iter = func->codeAnnotations.find(expr);
if (iter == func->codeAnnotations.end()) {
// No annotations at all.
return {};
}
return iter->second.branchLikely;
}
// Set the branch hint for an expression, trampling anything existing before.
inline void set(Expression* expr, std::optional<bool> likely, Function* func) {
// When we are writing an empty hint, do not create an empty annotation if one
// did not exist.
if (!likely && !func->codeAnnotations.count(expr)) {
return;
}
func->codeAnnotations[expr].branchLikely = likely;
}
// Clear the branch hint for an expression.
inline void clear(Expression* expr, Function* func) {
// Do not create an empty annotation if one did not exist.
auto iter = func->codeAnnotations.find(expr);
if (iter == func->codeAnnotations.end()) {
return;
}
iter->second.branchLikely = {};
}
// Copy the branch hint for an expression to another, trampling anything
// existing before for the latter.
inline void copyTo(Expression* from, Expression* to, Function* func) {
auto fromLikely = get(from, func);
set(to, fromLikely, func);
}
// Flip the branch hint for an expression (if it exists).
inline void flip(Expression* expr, Function* func) {
if (auto likely = get(expr, func)) {
set(expr, !*likely, func);
}
}
// Copy the branch hint for an expression to another, flipping it while we do
// so.
inline void copyFlippedTo(Expression* from, Expression* to, Function* func) {
copyTo(from, to, func);
flip(to, func);
}
// Given two expressions to read from, apply the AND hint to a target. That is,
// the target will be true when both inputs are true. |to| may be equal to
// |from1| or |from2|. The hint of |to| is trampled.
inline void applyAndTo(Expression* from1,
Expression* from2,
Expression* to,
Function* func) {
// If from1 and from2 are both likely, then from1 && from2 is slightly less
// likely, but we assume our hints are nearly certain, so we apply it. And,
// converse, if from1 and from2 and both unlikely, then from1 && from2 is even
// less likely, so we can once more apply a hint. If the hints differ, then
// one is unlikely or unknown, and we can't say anything about from1 && from2.
auto from1Hint = BranchHints::get(from1, func);
auto from2Hint = BranchHints::get(from2, func);
if (from1Hint == from2Hint) {
set(to, from1Hint, func);
} else {
// The hints do not even match.
BranchHints::clear(to, func);
}
}
// As |applyAndTo|, but now the condition on |to| the OR of |from1| and |from2|.
inline void applyOrTo(Expression* from1,
Expression* from2,
Expression* to,
Function* func) {
// If one is likely then so is from1 || from2. If both are unlikely then
// from1 || from2 is slightly more likely, but we assume our hints are nearly
// certain, so we apply it.
auto from1Hint = BranchHints::get(from1, func);
auto from2Hint = BranchHints::get(from2, func);
if ((from1Hint && *from1Hint) || (from2Hint && *from2Hint)) {
set(to, true, func);
} else if (from1Hint && from2Hint) {
// We ruled out that either one is present and true, so if both are present,
// both must be false.
assert(!*from1Hint && !*from2Hint);
set(to, false, func);
} else {
// We don't know.
BranchHints::clear(to, func);
}
}
} // namespace wasm::BranchHints
#endif // wasm_ir_branch_hint_h