/* * Copyright 2022 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_possible_constant_h #define wasm_ir_possible_constant_h #include #include "ir/properties.h" #include "wasm-builder.h" #include "wasm.h" namespace wasm { // Represents data about what constant values are possible in a particular // place. There may be no values, or one, or many, or if a non-constant value is // possible, then all we can say is that the value is "unknown" - it can be // anything. The values can either be literal values (Literal) or the names of // immutable globals (Name). // // Currently this just looks for a single constant value, and even two constant // values are treated as unknown. It may be worth optimizing more than that TODO struct PossibleConstantValues { private: // No possible value. struct None : public std::monostate {}; // Many possible values, and so this represents unknown data: we cannot infer // anything there. struct Many : public std::monostate {}; using Variant = std::variant; Variant value; public: PossibleConstantValues() : value(None()) {} bool operator==(const PossibleConstantValues& other) const { return value == other.value; } // Notes the contents of an expression and update our internal knowledge based // on it and all previous values noted. void note(Expression* expr, Module& wasm) { // If this is a constant literal value, note that. if (Properties::isConstantExpression(expr)) { note(Properties::getLiteral(expr)); return; } // If this is an immutable global that we get, note that. if (auto* get = expr->dynCast()) { auto* global = wasm.getGlobal(get->name); if (global->mutable_ == Immutable) { note(get->name); return; } } // Otherwise, this is not something we can reason about. noteUnknown(); } // Note either a Literal or a Name. template void note(T curr) { PossibleConstantValues other; other.value = curr; combine(other); } // Notes a value that is unknown - it can be anything. We have failed to // identify a constant value here. void noteUnknown() { value = Many(); } // Combine the information in a given PossibleConstantValues to this one. This // is the same as if we have called note*() on us with all the history of // calls to that other object. // // Returns whether we changed anything. bool combine(const PossibleConstantValues& other) { if (std::get_if(&other.value)) { return false; } if (std::get_if(&value)) { value = other.value; return true; } if (std::get_if(&value)) { return false; } if (other.value != value) { value = Many(); return true; } // Nulls compare equal, and we could consider any of the input nulls as the // combination of the two (as any of them would be valid to place in the // location we are working to optimize). In order to have simple symmetric // behavior here, which does not depend on the order of the inputs, use the // LUB. if (isNull() && other.isNull()) { auto type = getConstantLiteral().type.getHeapType(); auto otherType = other.getConstantLiteral().type.getHeapType(); auto lub = HeapType::getLeastUpperBound(type, otherType); if (!lub) { // TODO: Remove this workaround once we have bottom types to assign to // null literals. value = Many(); return true; } if (*lub != type) { value = Literal::makeNull(*lub); return true; } return false; } return false; } // Check if all the values are identical and constant. bool isConstant() const { return !std::get_if(&value) && !std::get_if(&value); } bool isConstantLiteral() const { return std::get_if(&value); } bool isConstantGlobal() const { return std::get_if(&value); } bool isNull() const { return isConstantLiteral() && getConstantLiteral().isNull(); } // Returns the single constant value. Literal getConstantLiteral() const { assert(isConstant()); return std::get(value); } Name getConstantGlobal() const { assert(isConstant()); return std::get(value); } // Assuming we have a single value, make an expression containing that value. Expression* makeExpression(Module& wasm) const { Builder builder(wasm); if (isConstantLiteral()) { return builder.makeConstantExpression(getConstantLiteral()); } else { auto name = getConstantGlobal(); return builder.makeGlobalGet(name, wasm.getGlobal(name)->type); } } // Returns whether we have ever noted a value. bool hasNoted() const { return !std::get_if(&value); } void dump(std::ostream& o) const { o << '['; if (!hasNoted()) { o << "unwritten"; } else if (!isConstant()) { o << "unknown"; } else if (isConstantLiteral()) { o << getConstantLiteral(); } else if (isConstantGlobal()) { o << '$' << getConstantGlobal(); } o << ']'; } }; } // namespace wasm #endif // wasm_ir_possible_constant_h