Skip to content

Commit aea55ab

Browse files
Axel-Reactorjeremy-lunarg
authored andcommitted
Fix linear time lookups in Builder::findScalarConstant
1 parent 6cfcfaf commit aea55ab

File tree

2 files changed

+118
-52
lines changed

2 files changed

+118
-52
lines changed

SPIRV/SpvBuilder.cpp

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,32 +1734,17 @@ bool Builder::containsPhysicalStorageBufferOrArray(Id typeId) const
17341734
// can be reused rather than duplicated. (Required by the specification).
17351735
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned value)
17361736
{
1737-
Instruction* constant;
1738-
for (int i = 0; i < (int)groupedConstants[enumCast(typeClass)].size(); ++i) {
1739-
constant = groupedConstants[enumCast(typeClass)][i];
1740-
if (constant->getOpCode() == opcode &&
1741-
constant->getTypeId() == typeId &&
1742-
constant->getImmediateOperand(0) == value)
1743-
return constant->getResultId();
1744-
}
1745-
1746-
return 0;
1737+
ScalarConstantKey key{ enumCast(typeClass), enumCast(opcode), typeId, value, 0 };
1738+
auto it = groupedScalarConstantResultIDs.find(key);
1739+
return (it != groupedScalarConstantResultIDs.end()) ? it->second : 0;
17471740
}
17481741

17491742
// Version of findScalarConstant (see above) for scalars that take two operands (e.g. a 'double' or 'int64').
17501743
Id Builder::findScalarConstant(Op typeClass, Op opcode, Id typeId, unsigned v1, unsigned v2)
17511744
{
1752-
Instruction* constant;
1753-
for (int i = 0; i < (int)groupedConstants[enumCast(typeClass)].size(); ++i) {
1754-
constant = groupedConstants[enumCast(typeClass)][i];
1755-
if (constant->getOpCode() == opcode &&
1756-
constant->getTypeId() == typeId &&
1757-
constant->getImmediateOperand(0) == v1 &&
1758-
constant->getImmediateOperand(1) == v2)
1759-
return constant->getResultId();
1760-
}
1761-
1762-
return 0;
1745+
ScalarConstantKey key{ enumCast(typeClass), enumCast(opcode), typeId, v1, v2 };
1746+
auto it = groupedScalarConstantResultIDs.find(key);
1747+
return (it != groupedScalarConstantResultIDs.end()) ? it->second : 0;
17631748
}
17641749

17651750
// Return true if consuming 'opcode' means consuming a constant.
@@ -1831,30 +1816,27 @@ Id Builder::makeNullConstant(Id typeId)
18311816
Id Builder::makeBoolConstant(bool b, bool specConstant)
18321817
{
18331818
Id typeId = makeBoolType();
1834-
Instruction* constant;
18351819
Op opcode = specConstant ? (b ? Op::OpSpecConstantTrue : Op::OpSpecConstantFalse) : (b ? Op::OpConstantTrue : Op::OpConstantFalse);
18361820

18371821
// See if we already made it. Applies only to regular constants, because specialization constants
18381822
// must remain distinct for the purpose of applying a SpecId decoration.
1839-
if (! specConstant) {
1840-
Id existing = 0;
1841-
for (int i = 0; i < (int)groupedConstants[enumCast(Op::OpTypeBool)].size(); ++i) {
1842-
constant = groupedConstants[enumCast(Op::OpTypeBool)][i];
1843-
if (constant->getTypeId() == typeId && constant->getOpCode() == opcode)
1844-
existing = constant->getResultId();
1845-
}
1846-
1823+
if (!specConstant) {
1824+
Id existing = findScalarConstant(Op::OpTypeBool, opcode, typeId, 0);
18471825
if (existing)
18481826
return existing;
18491827
}
18501828

18511829
// Make it
18521830
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
18531831
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1854-
groupedConstants[enumCast(Op::OpTypeBool)].push_back(c);
18551832
module.mapInstruction(c);
18561833

1857-
return c->getResultId();
1834+
Id resultId = c->getResultId();
1835+
if (!specConstant) {
1836+
ScalarConstantKey key{enumCast(Op::OpTypeBool), enumCast(opcode), typeId, 0, 0};
1837+
groupedScalarConstantResultIDs[key] = resultId;
1838+
}
1839+
return resultId;
18581840
}
18591841

18601842
Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
@@ -1872,10 +1854,14 @@ Id Builder::makeIntConstant(Id typeId, unsigned value, bool specConstant)
18721854
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
18731855
c->addImmediateOperand(value);
18741856
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1875-
groupedConstants[enumCast(Op::OpTypeInt)].push_back(c);
18761857
module.mapInstruction(c);
18771858

1878-
return c->getResultId();
1859+
Id resultId = c->getResultId();
1860+
if (!specConstant) {
1861+
ScalarConstantKey key{ enumCast(Op::OpTypeInt), enumCast(opcode), typeId, value, 0 };
1862+
groupedScalarConstantResultIDs[key] = resultId;
1863+
}
1864+
return resultId;
18791865
}
18801866

18811867
Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specConstant)
@@ -1898,10 +1884,14 @@ Id Builder::makeInt64Constant(Id typeId, unsigned long long value, bool specCons
18981884
c->addImmediateOperand(op1);
18991885
c->addImmediateOperand(op2);
19001886
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1901-
groupedConstants[enumCast(Op::OpTypeInt)].push_back(c);
19021887
module.mapInstruction(c);
19031888

1904-
return c->getResultId();
1889+
Id resultId = c->getResultId();
1890+
if (!specConstant) {
1891+
ScalarConstantKey key{ enumCast(Op::OpTypeInt), enumCast(opcode), typeId, op1, op2 };
1892+
groupedScalarConstantResultIDs[key] = resultId;
1893+
}
1894+
return resultId;
19051895
}
19061896

19071897
Id Builder::makeFloatConstant(float f, bool specConstant)
@@ -1923,10 +1913,14 @@ Id Builder::makeFloatConstant(float f, bool specConstant)
19231913
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
19241914
c->addImmediateOperand(value);
19251915
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1926-
groupedConstants[enumCast(Op::OpTypeFloat)].push_back(c);
19271916
module.mapInstruction(c);
19281917

1929-
return c->getResultId();
1918+
Id resultId = c->getResultId();
1919+
if (!specConstant) {
1920+
ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0 };
1921+
groupedScalarConstantResultIDs[key] = resultId;
1922+
}
1923+
return resultId;
19301924
}
19311925

19321926
Id Builder::makeDoubleConstant(double d, bool specConstant)
@@ -1952,10 +1946,14 @@ Id Builder::makeDoubleConstant(double d, bool specConstant)
19521946
c->addImmediateOperand(op1);
19531947
c->addImmediateOperand(op2);
19541948
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1955-
groupedConstants[enumCast(Op::OpTypeFloat)].push_back(c);
19561949
module.mapInstruction(c);
19571950

1958-
return c->getResultId();
1951+
Id resultId = c->getResultId();
1952+
if (!specConstant) {
1953+
ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, op1, op2 };
1954+
groupedScalarConstantResultIDs[key] = resultId;
1955+
}
1956+
return resultId;
19591957
}
19601958

19611959
Id Builder::makeFloat16Constant(float f16, bool specConstant)
@@ -1980,10 +1978,14 @@ Id Builder::makeFloat16Constant(float f16, bool specConstant)
19801978
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
19811979
c->addImmediateOperand(value);
19821980
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
1983-
groupedConstants[enumCast(Op::OpTypeFloat)].push_back(c);
19841981
module.mapInstruction(c);
19851982

1986-
return c->getResultId();
1983+
Id resultId = c->getResultId();
1984+
if (!specConstant) {
1985+
ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0 };
1986+
groupedScalarConstantResultIDs[key] = resultId;
1987+
}
1988+
return resultId;
19871989
}
19881990

19891991
Id Builder::makeBFloat16Constant(float bf16, bool specConstant)
@@ -2011,10 +2013,14 @@ Id Builder::makeBFloat16Constant(float bf16, bool specConstant)
20112013
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
20122014
c->addImmediateOperand(value);
20132015
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
2014-
groupedConstants[enumCast(Op::OpTypeFloat)].push_back(c);
20152016
module.mapInstruction(c);
20162017

2017-
return c->getResultId();
2018+
Id resultId = c->getResultId();
2019+
if (!specConstant) {
2020+
ScalarConstantKey key{ enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0 };
2021+
groupedScalarConstantResultIDs[key] = resultId;
2022+
}
2023+
return resultId;
20182024
}
20192025

20202026
Id Builder::makeFloatE5M2Constant(float fe5m2, bool specConstant)
@@ -2039,10 +2045,14 @@ Id Builder::makeFloatE5M2Constant(float fe5m2, bool specConstant)
20392045
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
20402046
c->addImmediateOperand(value);
20412047
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
2042-
groupedConstants[enumCast(Op::OpTypeFloat)].push_back(c);
20432048
module.mapInstruction(c);
20442049

2045-
return c->getResultId();
2050+
Id resultId = c->getResultId();
2051+
if (!specConstant) {
2052+
ScalarConstantKey key{enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0};
2053+
groupedScalarConstantResultIDs[key] = resultId;
2054+
}
2055+
return resultId;
20462056
}
20472057

20482058
Id Builder::makeFloatE4M3Constant(float fe4m3, bool specConstant)
@@ -2067,10 +2077,14 @@ Id Builder::makeFloatE4M3Constant(float fe4m3, bool specConstant)
20672077
Instruction* c = new Instruction(getUniqueId(), typeId, opcode);
20682078
c->addImmediateOperand(value);
20692079
constantsTypesGlobals.push_back(std::unique_ptr<Instruction>(c));
2070-
groupedConstants[enumCast(Op::OpTypeFloat)].push_back(c);
20712080
module.mapInstruction(c);
20722081

2073-
return c->getResultId();
2082+
Id resultId = c->getResultId();
2083+
if (!specConstant) {
2084+
ScalarConstantKey key{enumCast(Op::OpTypeFloat), enumCast(opcode), typeId, value, 0};
2085+
groupedScalarConstantResultIDs[key] = resultId;
2086+
}
2087+
return resultId;
20742088
}
20752089

20762090
Id Builder::makeFpConstant(Id type, double d, bool specConstant)
@@ -2111,8 +2125,8 @@ Id Builder::findCompositeConstant(Op typeClass, Op opcode, Id typeId, const std:
21112125
{
21122126
Instruction* constant = nullptr;
21132127
bool found = false;
2114-
for (int i = 0; i < (int)groupedConstants[enumCast(typeClass)].size(); ++i) {
2115-
constant = groupedConstants[enumCast(typeClass)][i];
2128+
for (int i = 0; i < (int)groupedCompositeConstants[enumCast(typeClass)].size(); ++i) {
2129+
constant = groupedCompositeConstants[enumCast(typeClass)][i];
21162130

21172131
if (constant->getTypeId() != typeId)
21182132
continue;
@@ -2222,7 +2236,7 @@ Id Builder::makeCompositeConstant(Id typeId, const std::vector<Id>& members, boo
22222236
if (typeClass == Op::OpTypeStruct)
22232237
groupedStructConstants[typeId].push_back(c);
22242238
else
2225-
groupedConstants[enumCast(typeClass)].push_back(c);
2239+
groupedCompositeConstants[enumCast(typeClass)].push_back(c);
22262240
module.mapInstruction(c);
22272241

22282242
return c->getResultId();

SPIRV/SpvBuilder.h

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1041,8 +1041,58 @@ class Builder {
10411041

10421042
// not output, internally used for quick & dirty canonical (unique) creation
10431043

1044+
// Key for scalar constants (handles both 32-bit and 64-bit)
1045+
struct ScalarConstantKey {
1046+
unsigned int typeClass; // OpTypeInt, OpTypeFloat, OpTypeBool
1047+
unsigned int opcode; // OpConstant, OpSpecConstant, OpConstantTrue, etc.
1048+
Id typeId; // The specific type
1049+
unsigned value1; // First operand (or only operand)
1050+
unsigned value2; // Second operand (0 for single-operand constants)
1051+
1052+
bool operator==(const ScalarConstantKey& other) const {
1053+
return typeClass == other.typeClass &&
1054+
opcode == other.opcode &&
1055+
typeId == other.typeId &&
1056+
value1 == other.value1 &&
1057+
value2 == other.value2;
1058+
}
1059+
};
1060+
1061+
struct ScalarConstantKeyHash {
1062+
// 64/32 bit mix function from MurmurHash3
1063+
inline std::size_t hash_mix(std::size_t h) const {
1064+
if constexpr (sizeof(std::size_t) == 8) {
1065+
h ^= h >> 33;
1066+
h *= UINT64_C(0xff51afd7ed558ccd);
1067+
h ^= h >> 33;
1068+
h *= UINT64_C(0xc4ceb9fe1a85ec53);
1069+
h ^= h >> 33;
1070+
return h;
1071+
} else {
1072+
h ^= h >> 16;
1073+
h *= UINT32_C(0x85ebca6b);
1074+
h ^= h >> 13;
1075+
h *= UINT32_C(0xc2b2ae35);
1076+
h ^= h >> 16;
1077+
return h;
1078+
}
1079+
}
1080+
1081+
// Hash combine from boost
1082+
inline std::size_t hash_combine(std::size_t seed, std::size_t v) const {
1083+
return hash_mix(seed + 0x9e3779b9 + v);
1084+
}
1085+
1086+
std::size_t operator()(const ScalarConstantKey& k) const {
1087+
size_t hash1 = hash_combine(std::hash<unsigned>{}(k.typeClass), std::hash<unsigned>{}(k.opcode));
1088+
size_t hash2 = hash_combine(std::hash<Id>{}(k.value1), std::hash<unsigned>{}(k.value2));
1089+
size_t hash3 = hash_combine(hash1, hash2);
1090+
return hash_combine(hash3, std::hash<unsigned>{}(k.typeId));
1091+
}
1092+
};
1093+
10441094
// map type opcodes to constant inst.
1045-
std::unordered_map<unsigned int, std::vector<Instruction*>> groupedConstants;
1095+
std::unordered_map<unsigned int, std::vector<Instruction*>> groupedCompositeConstants;
10461096
// map struct-id to constant instructions
10471097
std::unordered_map<unsigned int, std::vector<Instruction*>> groupedStructConstants;
10481098
// map type opcodes to type instructions
@@ -1051,6 +1101,8 @@ class Builder {
10511101
std::unordered_map<unsigned int, std::vector<Instruction*>> groupedDebugTypes;
10521102
// list of OpConstantNull instructions
10531103
std::vector<Instruction*> nullConstants;
1104+
// map scalar constants to result IDs
1105+
std::unordered_map<ScalarConstantKey, Id, ScalarConstantKeyHash> groupedScalarConstantResultIDs;
10541106

10551107
// Track which types have explicit layouts, to avoid reusing in storage classes without layout.
10561108
// Currently only tracks array types.

0 commit comments

Comments
 (0)