Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/Compiler/src/TableShape.cpp
2725 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "TableShape.h"
3
4
namespace Luau
5
{
6
namespace Compile
7
{
8
9
// conservative limit for the loop bound that establishes table array size
10
static const int kMaxLoopBound = 16;
11
12
static AstExprTable* getTableHint(AstExpr* expr)
13
{
14
// unadorned table literal
15
if (AstExprTable* table = expr->as<AstExprTable>())
16
return table;
17
18
// setmetatable(table literal, ...)
19
if (AstExprCall* call = expr->as<AstExprCall>(); call && !call->self && call->args.size == 2)
20
if (AstExprGlobal* func = call->func->as<AstExprGlobal>(); func && func->name == "setmetatable")
21
if (AstExprTable* table = call->args.data[0]->as<AstExprTable>())
22
return table;
23
24
return nullptr;
25
}
26
27
struct ShapeVisitor : AstVisitor
28
{
29
struct Hasher
30
{
31
size_t operator()(const std::pair<AstExprTable*, AstName>& p) const
32
{
33
return DenseHashPointer()(p.first) ^ std::hash<AstName>()(p.second);
34
}
35
};
36
37
DenseHashMap<AstExprTable*, TableShape>& shapes;
38
39
DenseHashMap<AstLocal*, AstExprTable*> tables;
40
DenseHashSet<std::pair<AstExprTable*, AstName>, Hasher> fields;
41
42
DenseHashMap<AstLocal*, unsigned int> loops; // iterator => upper bound for 1..k
43
44
ShapeVisitor(DenseHashMap<AstExprTable*, TableShape>& shapes)
45
: shapes(shapes)
46
, tables(nullptr)
47
, fields(std::pair<AstExprTable*, AstName>())
48
, loops(nullptr)
49
{
50
}
51
52
void assignField(AstExpr* expr, AstName index)
53
{
54
if (AstExprLocal* lv = expr->as<AstExprLocal>())
55
{
56
if (AstExprTable** table = tables.find(lv->local))
57
{
58
std::pair<AstExprTable*, AstName> field = {*table, index};
59
60
if (!fields.contains(field))
61
{
62
fields.insert(field);
63
shapes[*table].hashSize += 1;
64
}
65
}
66
}
67
}
68
69
void assignField(AstExpr* expr, AstExpr* index)
70
{
71
AstExprLocal* lv = expr->as<AstExprLocal>();
72
if (!lv)
73
return;
74
75
AstExprTable** table = tables.find(lv->local);
76
if (!table)
77
return;
78
79
if (AstExprConstantNumber* number = index->as<AstExprConstantNumber>())
80
{
81
TableShape& shape = shapes[*table];
82
83
if (number->value == double(shape.arraySize + 1))
84
shape.arraySize += 1;
85
}
86
else if (AstExprLocal* iter = index->as<AstExprLocal>())
87
{
88
if (const unsigned int* bound = loops.find(iter->local))
89
{
90
TableShape& shape = shapes[*table];
91
92
if (shape.arraySize == 0)
93
shape.arraySize = *bound;
94
}
95
}
96
}
97
98
void assign(AstExpr* var)
99
{
100
if (AstExprIndexName* index = var->as<AstExprIndexName>())
101
{
102
assignField(index->expr, index->index);
103
}
104
else if (AstExprIndexExpr* index = var->as<AstExprIndexExpr>())
105
{
106
assignField(index->expr, index->index);
107
}
108
}
109
110
bool visit(AstStatLocal* node) override
111
{
112
// track local -> table association so that we can update table size prediction in assignField
113
if (node->vars.size == 1 && node->values.size == 1)
114
if (AstExprTable* table = getTableHint(node->values.data[0]); table && table->items.size == 0)
115
tables[node->vars.data[0]] = table;
116
117
return true;
118
}
119
120
bool visit(AstStatAssign* node) override
121
{
122
for (size_t i = 0; i < node->vars.size; ++i)
123
assign(node->vars.data[i]);
124
125
for (size_t i = 0; i < node->values.size; ++i)
126
node->values.data[i]->visit(this);
127
128
return false;
129
}
130
131
bool visit(AstStatFunction* node) override
132
{
133
assign(node->name);
134
node->func->visit(this);
135
136
return false;
137
}
138
139
bool visit(AstStatFor* node) override
140
{
141
AstExprConstantNumber* from = node->from->as<AstExprConstantNumber>();
142
AstExprConstantNumber* to = node->to->as<AstExprConstantNumber>();
143
144
if (from && to && from->value == 1.0 && to->value >= 1.0 && to->value <= double(kMaxLoopBound) && !node->step)
145
loops[node->var] = unsigned(to->value);
146
147
return true;
148
}
149
};
150
151
void predictTableShapes(DenseHashMap<AstExprTable*, TableShape>& shapes, AstNode* root)
152
{
153
ShapeVisitor visitor{shapes};
154
root->visit(&visitor);
155
}
156
157
} // namespace Compile
158
} // namespace Luau
159
160