1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <cstddef>
#include "common/common_types.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::Optimization {
constexpr u32 MaskCanBeMinusOne{1u << 0};
constexpr u32 MaskCanBeZero{1u << 1};
constexpr u32 MaskCanBeOne{1u << 2};
constexpr u32 MaskUnknown{0};
constexpr u32 MaxMaskAnalysisDepth{8};
// negation maps -1 -> 1, 0 -> 0 and 1 -> -1: swap the -1 and 1 bits, keep 0
inline u32 NegateMask(u32 values) {
return (values & MaskCanBeZero) | ((values & MaskCanBeMinusOne) << 2) |
((values & MaskCanBeOne) >> 2);
}
// x + 1 with x != 1: -1 becomes 0, 0 becomes 1
inline u32 AddOneToMask(u32 values) {
return values << 1;
}
// x - 1 with x != -1: 0 becomes -1, 1 becomes 0
inline u32 SubtractOneFromMask(u32 values) {
return values >> 1;
}
// mask constant: guest code encodes booleans as 0, 1 and -1 (all bits set)
inline bool IsMaskConstant(const IR::Value& candidate) {
return candidate.IsImmediate() &&
(candidate.U32() == 0 || candidate.U32() == 1 || candidate.U32() == 0xffffffffU);
}
/// "predicate mask" can take: -1, 0 and 1.
/// guest compilers build masks from selects, negations, +/-1 adjustments and phis.
/// everything unproven comes back as MaskUnknown, so callers only fold
/// with a proven mask and never fold on a wrong one.
inline u32 MaskValues(const IR::Value& value, u32 depth = 0) {
const IR::Value resolved{value.Resolve()};
if (resolved.IsImmediate()) {
if (resolved.Type() != IR::Type::U32) {
return MaskUnknown;
}
switch (resolved.U32()) {
case 0xffffffffU:
return MaskCanBeMinusOne;
case 0:
return MaskCanBeZero;
case 1:
return MaskCanBeOne;
default:
return MaskUnknown;
}
}
if (depth >= MaxMaskAnalysisDepth) {
return MaskUnknown;
}
const IR::Inst* const inst{resolved.InstRecursive()};
switch (inst->GetOpcode()) {
case IR::Opcode::SelectU32: {
const u32 true_values{MaskValues(inst->Arg(1), depth + 1)};
const u32 false_values{MaskValues(inst->Arg(2), depth + 1)};
return true_values != MaskUnknown && false_values != MaskUnknown
? true_values | false_values
: MaskUnknown;
}
case IR::Opcode::INeg32:
return NegateMask(MaskValues(inst->Arg(0), depth + 1));
case IR::Opcode::IAdd32:
for (size_t index = 0; index < 2; ++index) {
const IR::Value step{inst->Arg(1 - index).Resolve()};
if (!step.IsImmediate()) {
continue;
}
const u32 values{MaskValues(inst->Arg(index), depth + 1)};
// +/- 1 stays in the domain only if the sum cannot escape it
// 1 + 1 = 2 and -1 - 1 = -2 are not mask values
if (step.U32() == 1 && (values & MaskCanBeOne) == 0) {
return AddOneToMask(values);
}
if (step.U32() == 0xffffffffU && (values & MaskCanBeMinusOne) == 0) {
return SubtractOneFromMask(values);
}
return MaskUnknown;
}
return MaskUnknown;
case IR::Opcode::Phi: {
u32 values{};
for (size_t index = 0; index < inst->NumArgs(); ++index) {
const u32 arg_values{MaskValues(inst->Arg(index), depth + 1)};
if (arg_values == MaskUnknown) {
return MaskUnknown;
}
values |= arg_values;
}
return values;
}
default:
return MaskUnknown;
}
}
} // namespace Shader::Optimization
|