mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-13 06:38:31 +09:00
[Perf] (MG_Util): replace the builtin-shadowing string scans with one tokenize and a SPIR-V OpName pass
RenameBuiltinShadowingFunction probed the whole source ten times per compile (5 names x mask + scan, each a full-text pass) and still had two blind spots: a 5-name list and single-line-definition-only detection. On Complementary-scale packs (4.5MB of sources) that was ~68% of the compile phase. The rename is now split by FAILURE LAYER, both halves sharing one name table header so they cannot drift: - A SPIR-V OpName pass in SanitizeAndOptimizeBinary covers the full ESSL 3.20 builtin set (~146 names). Renaming a function id is safe by construction: builtin calls are GLSL.std.450 instructions and can never resolve to a user OpFunction, overloads are distinct ids (a helper overload delegating to the real builtin keeps working), dead preprocessor branches never reach SPIR-V, and macro-expanded definitions are covered. ESSL 3.x is the only consumer that forbids the redefinitions, and this pass runs before its transpile. - A lexical pass covers only the 5 names whose exact-signature redefinitions glslang's relaxed parse rejects outright (never producing SPIR-V for the backstop): the historical fma/max3/min3/round/tanh. One TokenizeCode pass; definition detection requires brace depth 0, a type-identifier previous token that is neither a statement keyword nor a directive tail, and skips files whose token-level braces do not balance (preprocessor-asymmetric arms) - over-detection is unrecoverable, so every ambiguity falls through to the backstop. Measured on the compile phase (prefix-diff, 3-run medians, Espryt/NVIDIA): complementary-reimagined 20.0s -> 5.5s, BSL 2.14s -> 1.85s. bliss (the pack that ships from-scratch fma/tanh helpers) stays at SSIM 0.999962. Tests: end-to-end ESSL assertions for the multiline-definition and new-overload shapes, the three adversarial-review reproductions (statement- keyword call under asymmetric braces, dead-#if compat shim, overload delegating to the shadowed builtin), and a source-level assertion pinning the lexical half specifically.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
// MobileGL - MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/RenameBuiltinShadowingFunctionsPass.cpp
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
#include "RenameBuiltinShadowingFunctionsPass.h"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "../EsslBuiltinFunctionNames.h"
|
||||
#include "spirv.hpp"
|
||||
#include "source/opt/def_use_manager.h"
|
||||
#include "source/opt/instruction.h"
|
||||
#include "source/opt/ir_context.h"
|
||||
#include "source/opt/module.h"
|
||||
#include "source/util/make_unique.h"
|
||||
|
||||
namespace MobileGL {
|
||||
namespace MG_Util {
|
||||
namespace ShaderTranspiler {
|
||||
spvtools::opt::Pass::Status RenameBuiltinShadowingFunctionsPass::Process() {
|
||||
Bool modified = false;
|
||||
auto* irContext = context();
|
||||
auto* defUseMgr = irContext->get_def_use_mgr();
|
||||
|
||||
for (auto& debugInst : irContext->debugs2()) {
|
||||
if (debugInst.opcode() != spv::Op::OpName || debugInst.NumInOperands() < 2) {
|
||||
continue;
|
||||
}
|
||||
const auto* target = defUseMgr->GetDef(debugInst.GetSingleWordInOperand(0));
|
||||
if (target == nullptr || target->opcode() != spv::Op::OpFunction) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// glslang mangles function OpNames as "name(<paramcodes>"; the base name is
|
||||
// everything before the '(' (entry points like "main" carry no mangling).
|
||||
const std::string mangled = debugInst.GetInOperand(1).AsString();
|
||||
const std::string_view baseName =
|
||||
std::string_view(mangled).substr(0, mangled.find('('));
|
||||
if (!IsEsslBuiltinFunctionName(baseName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
debugInst.SetInOperand(1, spvtools::utils::MakeVector<spvtools::opt::Operand::OperandData>(
|
||||
"mg_" + mangled));
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
|
||||
}
|
||||
|
||||
spvtools::Optimizer::PassToken
|
||||
RenameBuiltinShadowingFunctionsPass::CreateRenameBuiltinShadowingFunctionsPass() {
|
||||
return spvtools::Optimizer::PassToken(MakeUnique<RenameBuiltinShadowingFunctionsPass>());
|
||||
}
|
||||
} // namespace ShaderTranspiler
|
||||
} // namespace MG_Util
|
||||
} // namespace MobileGL
|
||||
@@ -0,0 +1,46 @@
|
||||
// MobileGL - MobileGL/MG_Util/ShaderTranspiler/SpirvPasses/RenameBuiltinShadowingFunctionsPass.h
|
||||
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||
// Licensed under the GNU Lesser General Public License v3.0:
|
||||
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
// End of Source File Header
|
||||
|
||||
#pragma once
|
||||
#include "source/opt/pass.h"
|
||||
#include "spirv-tools/optimizer.hpp"
|
||||
|
||||
#include <Includes.h>
|
||||
|
||||
namespace MobileGL {
|
||||
namespace MG_Util {
|
||||
namespace ShaderTranspiler {
|
||||
// Desktop GLSL lets a shader redefine a builtin function (round, fma, ...) and
|
||||
// shadow it; ESSL 3.x forbids the redefinition, so when SPIRV-Cross re-emits the
|
||||
// function under its original OpName a strict ES driver rejects the shader with a
|
||||
// redefinition error. Prefix the OpName of every user-defined function whose base
|
||||
// name collides with an ESSL builtin (plus the min3/max3 trinary extension names)
|
||||
// with "mg_". Renaming a user function is always semantics-preserving: its
|
||||
// definition and every call site go through the same result id, while calls to
|
||||
// the real builtin never resolve to a user function id in SPIR-V.
|
||||
//
|
||||
// This is the BACKSTOP half of the rename. The primary half is the lexical
|
||||
// RenameBuiltinShadowingFunctions in ShaderSourceProcessor, which has to run
|
||||
// before the parse - glslang's relaxed parse rejects some shadowing overload
|
||||
// shapes outright, and a shadowed builtin may itself need an extension the
|
||||
// declared #version does not enable. This pass catches what a lexical scan
|
||||
// cannot see (macro-expanded definitions) and is idempotent: an already
|
||||
// renamed mg_* name is not in the builtin table.
|
||||
//
|
||||
// Both halves share MG_Util/ShaderTranspiler/EsslBuiltinFunctionNames.h, so the
|
||||
// covered name set cannot drift between them.
|
||||
class RenameBuiltinShadowingFunctionsPass : public spvtools::opt::Pass {
|
||||
public:
|
||||
const char* name() const override { return "rename-builtin-shadowing-functions"; }
|
||||
Status Process() override;
|
||||
|
||||
static spvtools::Optimizer::PassToken CreateRenameBuiltinShadowingFunctionsPass();
|
||||
};
|
||||
} // namespace ShaderTranspiler
|
||||
} // namespace MG_Util
|
||||
} // namespace MobileGL
|
||||
Reference in New Issue
Block a user