Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/CodeGen/src/CodeGenAssembly.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 "Luau/BytecodeAnalysis.h"
3
#include "Luau/BytecodeSummary.h"
4
#include "Luau/IrDump.h"
5
#include "Luau/IrUtils.h"
6
7
#include "CodeGenLower.h"
8
9
#include "CodeGenA64.h"
10
#include "CodeGenX64.h"
11
12
#include "lapi.h"
13
14
namespace Luau
15
{
16
namespace CodeGen
17
{
18
19
static const LocVar* tryFindLocal(const Proto* proto, int reg, int pcpos)
20
{
21
for (int i = 0; i < proto->sizelocvars; i++)
22
{
23
const LocVar& local = proto->locvars[i];
24
25
if (reg == local.reg && pcpos >= local.startpc && pcpos < local.endpc)
26
return &local;
27
}
28
29
return nullptr;
30
}
31
32
const char* tryFindLocalName(const Proto* proto, int reg, int pcpos)
33
{
34
const LocVar* var = tryFindLocal(proto, reg, pcpos);
35
36
if (var && var->varname)
37
return getstr(var->varname);
38
39
return nullptr;
40
}
41
42
const char* tryFindUpvalueName(const Proto* proto, int upval)
43
{
44
if (proto->upvalues)
45
{
46
CODEGEN_ASSERT(upval < proto->sizeupvalues);
47
48
if (proto->upvalues[upval])
49
return getstr(proto->upvalues[upval]);
50
}
51
52
return nullptr;
53
}
54
55
template<typename AssemblyBuilder>
56
static void logFunctionHeader(AssemblyBuilder& build, Proto* proto)
57
{
58
if (proto->debugname)
59
build.logAppend("; function %s(", getstr(proto->debugname));
60
else
61
build.logAppend("; function(");
62
63
for (int i = 0; i < proto->numparams; i++)
64
{
65
if (const char* name = tryFindLocalName(proto, i, 0))
66
build.logAppend("%s%s", i == 0 ? "" : ", ", name);
67
else
68
build.logAppend("%s$arg%d", i == 0 ? "" : ", ", i);
69
}
70
71
if (proto->numparams != 0 && proto->is_vararg)
72
build.logAppend(", ...)");
73
else
74
build.logAppend(")");
75
76
if (proto->linedefined >= 0)
77
build.logAppend(" line %d\n", proto->linedefined);
78
else
79
build.logAppend("\n");
80
}
81
82
template<typename AssemblyBuilder>
83
static void logFunctionTypes(AssemblyBuilder& build, const IrFunction& function, const char* const* userdataTypes)
84
{
85
const BytecodeTypeInfo& typeInfo = function.bcTypeInfo;
86
87
for (size_t i = 0; i < typeInfo.argumentTypes.size(); i++)
88
{
89
uint8_t ty = typeInfo.argumentTypes[i];
90
91
const char* type = getBytecodeTypeName(ty, userdataTypes);
92
const char* optional = (ty & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "";
93
94
if (ty != LBC_TYPE_ANY)
95
{
96
if (const char* name = tryFindLocalName(function.proto, int(i), 0))
97
build.logAppend("; R%d: %s%s [argument '%s']\n", int(i), type, optional, name);
98
else
99
build.logAppend("; R%d: %s%s [argument]\n", int(i), type, optional);
100
}
101
}
102
103
for (size_t i = 0; i < typeInfo.upvalueTypes.size(); i++)
104
{
105
uint8_t ty = typeInfo.upvalueTypes[i];
106
107
const char* type = getBytecodeTypeName(ty, userdataTypes);
108
const char* optional = (ty & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "";
109
110
if (ty != LBC_TYPE_ANY)
111
{
112
if (const char* name = tryFindUpvalueName(function.proto, int(i)))
113
build.logAppend("; U%d: %s%s ['%s']\n", int(i), type, optional, name);
114
else
115
build.logAppend("; U%d: %s%s\n", int(i), type, optional);
116
}
117
}
118
119
for (const BytecodeRegTypeInfo& el : typeInfo.regTypes)
120
{
121
const char* type = getBytecodeTypeName(el.type, userdataTypes);
122
const char* optional = (el.type & LBC_TYPE_OPTIONAL_BIT) != 0 ? "?" : "";
123
124
// Using last active position as the PC because 'startpc' for type info is before local is initialized
125
if (const char* name = tryFindLocalName(function.proto, el.reg, el.endpc - 1))
126
build.logAppend("; R%d: %s%s from %d to %d [local '%s']\n", el.reg, type, optional, el.startpc, el.endpc, name);
127
else
128
build.logAppend("; R%d: %s%s from %d to %d\n", el.reg, type, optional, el.startpc, el.endpc);
129
}
130
}
131
132
unsigned getInstructionCount(const Instruction* insns, const unsigned size)
133
{
134
unsigned count = 0;
135
for (unsigned i = 0; i < size;)
136
{
137
++count;
138
i += getOpLength(LuauOpcode(LUAU_INSN_OP(insns[i])));
139
}
140
return count;
141
}
142
143
template<typename AssemblyBuilder>
144
static std::string getAssemblyImpl(AssemblyBuilder& build, const TValue* func, AssemblyOptions options, LoweringStats* stats)
145
{
146
Proto* root = clvalue(func)->l.p;
147
148
if ((options.compilationOptions.flags & CodeGen_OnlyNativeModules) != 0 && (root->flags & LPF_NATIVE_MODULE) == 0)
149
{
150
build.finalize();
151
return std::string();
152
}
153
154
std::vector<Proto*> protos;
155
gatherFunctions(protos, root, options.compilationOptions.flags, root->flags & LPF_NATIVE_FUNCTION);
156
157
protos.erase(
158
std::remove_if(
159
protos.begin(),
160
protos.end(),
161
[](Proto* p)
162
{
163
return p == nullptr;
164
}
165
),
166
protos.end()
167
);
168
169
if (stats)
170
stats->totalFunctions += unsigned(protos.size());
171
172
if (protos.empty())
173
{
174
build.finalize();
175
return std::string();
176
}
177
178
ModuleHelpers helpers;
179
assembleHelpers(build, helpers);
180
181
if (!options.includeOutlinedCode && options.includeAssembly)
182
{
183
build.text.clear();
184
build.logAppend("; skipping %u bytes of outlined helpers\n", unsigned(build.getCodeSize() * sizeof(build.code[0])));
185
}
186
187
for (Proto* p : protos)
188
{
189
IrBuilder ir(options.compilationOptions.hooks);
190
ir.buildFunctionIr(p);
191
unsigned asmSize = build.getCodeSize();
192
unsigned asmCount = build.getInstructionCount();
193
194
if (options.includeAssembly || options.includeIr)
195
logFunctionHeader(build, p);
196
197
if (options.includeIrTypes)
198
logFunctionTypes(build, ir.function, options.compilationOptions.userdataTypes);
199
200
CodeGenCompilationResult result = CodeGenCompilationResult::Success;
201
202
if (!lowerFunction(ir, build, helpers, p, options, stats, result))
203
{
204
if (build.logText)
205
build.logAppend("; skipping (can't lower)\n");
206
207
asmSize = 0;
208
asmCount = 0;
209
210
if (stats)
211
stats->skippedFunctions += 1;
212
}
213
else
214
{
215
asmSize = build.getCodeSize() - asmSize;
216
asmCount = build.getInstructionCount() - asmCount;
217
}
218
219
if (stats && (stats->functionStatsFlags & FunctionStats_Enable))
220
{
221
FunctionStats functionStat;
222
223
// function name is empty for anonymous and pseudo top-level functions
224
// properly name pseudo top-level function because it will be compiled natively if it has loops
225
functionStat.name = p->debugname ? getstr(p->debugname) : p->bytecodeid == root->bytecodeid ? "[top level]" : "[anonymous]";
226
functionStat.line = p->linedefined;
227
functionStat.bcodeCount = getInstructionCount(p->code, p->sizecode);
228
functionStat.irCount = unsigned(ir.function.instructions.size());
229
functionStat.asmSize = asmSize * sizeof(build.code[0]);
230
functionStat.asmCount = asmCount;
231
if (stats->functionStatsFlags & FunctionStats_BytecodeSummary)
232
{
233
FunctionBytecodeSummary summary(FunctionBytecodeSummary::fromProto(p, 0));
234
functionStat.bytecodeSummary.push_back(summary.getCounts(0));
235
}
236
stats->functions.push_back(std::move(functionStat));
237
}
238
239
if (build.logText)
240
build.logAppend("\n");
241
}
242
243
if (!build.finalize())
244
return std::string();
245
246
if (options.outputBinary)
247
return std::string(reinterpret_cast<const char*>(build.code.data()), reinterpret_cast<const char*>(build.code.data() + build.code.size())) +
248
std::string(build.data.begin(), build.data.end());
249
else
250
return build.text;
251
}
252
253
#if defined(CODEGEN_TARGET_A64)
254
unsigned int getCpuFeaturesA64();
255
#else
256
unsigned int getCpuFeaturesX64();
257
#endif
258
259
std::string getAssembly(lua_State* L, int idx, AssemblyOptions options, LoweringStats* stats)
260
{
261
CODEGEN_ASSERT(lua_isLfunction(L, idx));
262
const TValue* func = luaA_toobject(L, idx);
263
264
switch (options.target)
265
{
266
case AssemblyOptions::Host:
267
{
268
#if defined(CODEGEN_TARGET_A64)
269
static unsigned int cpuFeatures = getCpuFeaturesA64();
270
A64::AssemblyBuilderA64 build(/* logText= */ options.includeAssembly, cpuFeatures);
271
#else
272
static unsigned int cpuFeatures = getCpuFeaturesX64();
273
X64::AssemblyBuilderX64 build(/* logText= */ options.includeAssembly, cpuFeatures);
274
#endif
275
276
return getAssemblyImpl(build, func, options, stats);
277
}
278
279
case AssemblyOptions::A64:
280
{
281
A64::AssemblyBuilderA64 build(/* logText= */ options.includeAssembly, /* features= */ A64::Feature_JSCVT);
282
283
return getAssemblyImpl(build, func, options, stats);
284
}
285
286
case AssemblyOptions::A64_NoFeatures:
287
{
288
A64::AssemblyBuilderA64 build(/* logText= */ options.includeAssembly, /* features= */ 0);
289
290
return getAssemblyImpl(build, func, options, stats);
291
}
292
293
case AssemblyOptions::X64_Windows:
294
{
295
X64::AssemblyBuilderX64 build(/* logText= */ options.includeAssembly, X64::ABIX64::Windows);
296
297
return getAssemblyImpl(build, func, options, stats);
298
}
299
300
case AssemblyOptions::X64_SystemV:
301
{
302
X64::AssemblyBuilderX64 build(/* logText= */ options.includeAssembly, X64::ABIX64::SystemV);
303
304
return getAssemblyImpl(build, func, options, stats);
305
}
306
307
default:
308
CODEGEN_ASSERT(!"Unknown target");
309
return std::string();
310
}
311
}
312
313
} // namespace CodeGen
314
} // namespace Luau
315
316