Sync to upstream/release/581 (#958)

* Definition files can now ascribe indexers to class types.
(https://github.com/Roblox/luau/pull/949)
* Remove --compile support from the REPL. You can just use luau-compile
instead.
* When an exception is thrown during parallel typechecking (usually an
ICE), we now gracefully stop typechecking and drain active workers
before rethrowing the exception.

New solver

* Include more source location information when we hit an internal
compiler error
* Improve the logic that simplifies intersections of tables

JIT

* Save testable type annotations to bytecode
* Improve block placement for linearized blocks
* Add support for lea reg, [rip+offset] for labels
* Unify X64 and A64 codegen for RETURN
* Outline interrupt handlers for X64
* Remove global rArgN in favor of build.abi
* Change A64 INTERRUPT lowering to match X64

---------

Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com>
This commit is contained in:
Andy Friesen 2023-06-16 10:35:18 -07:00 committed by GitHub
parent bc0722471f
commit d458d240cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 969 additions and 613 deletions

View File

@ -174,10 +174,10 @@ struct ConstraintSolver
bool blockOnPendingTypes(TypePackId target, NotNull<const Constraint> constraint);
void unblock(NotNull<const Constraint> progressed);
void unblock(TypeId progressed);
void unblock(TypePackId progressed);
void unblock(const std::vector<TypeId>& types);
void unblock(const std::vector<TypePackId>& packs);
void unblock(TypeId progressed, Location location);
void unblock(TypePackId progressed, Location location);
void unblock(const std::vector<TypeId>& types, Location location);
void unblock(const std::vector<TypePackId>& packs, Location location);
/**
* @returns true if the TypeId is in a blocked state.

View File

@ -539,8 +539,8 @@ bool ConstraintSolver::tryDispatch(const GeneralizationConstraint& c, NotNull<co
asMutable(c.generalizedType)->ty.emplace<BoundType>(builtinTypes->errorRecoveryType());
}
unblock(c.generalizedType);
unblock(c.sourceType);
unblock(c.generalizedType, constraint->location);
unblock(c.sourceType, constraint->location);
return true;
}
@ -564,7 +564,7 @@ bool ConstraintSolver::tryDispatch(const InstantiationConstraint& c, NotNull<con
reportError(UnificationTooComplex{}, constraint->location);
asMutable(c.subType)->ty.emplace<BoundType>(errorRecoveryType());
unblock(c.subType);
unblock(c.subType, constraint->location);
return true;
}
@ -574,7 +574,7 @@ bool ConstraintSolver::tryDispatch(const InstantiationConstraint& c, NotNull<con
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
queuer.traverse(c.subType);
unblock(c.subType);
unblock(c.subType, constraint->location);
return true;
}
@ -597,7 +597,7 @@ bool ConstraintSolver::tryDispatch(const UnaryConstraint& c, NotNull<const Const
{
asMutable(c.resultType)->ty.emplace<BoundType>(builtinTypes->booleanType);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
case AstExprUnary::Len:
@ -605,7 +605,7 @@ bool ConstraintSolver::tryDispatch(const UnaryConstraint& c, NotNull<const Const
// __len must return a number.
asMutable(c.resultType)->ty.emplace<BoundType>(builtinTypes->numberType);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
case AstExprUnary::Minus:
@ -635,7 +635,7 @@ bool ConstraintSolver::tryDispatch(const UnaryConstraint& c, NotNull<const Const
asMutable(c.resultType)->ty.emplace<BoundType>(builtinTypes->errorRecoveryType());
}
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
}
@ -684,7 +684,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
if (isBlocked(leftType) || (hasTypeInIntersection<FreeType>(leftType) && !isLogical))
{
asMutable(resultType)->ty.emplace<BoundType>(errorRecoveryType());
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
@ -697,7 +697,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
{
// TODO: Boolean singleton false? The result is _always_ boolean false.
asMutable(resultType)->ty.emplace<BoundType>(builtinTypes->booleanType);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
@ -760,7 +760,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
}
asMutable(resultType)->ty.emplace<BoundType>(mmResult);
unblock(resultType);
unblock(resultType, constraint->location);
(*c.astOriginalCallTypes)[c.astFragment] = *mm;
(*c.astOverloadResolvedTypes)[c.astFragment] = *instantiatedMm;
@ -790,14 +790,14 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
{
unify(leftType, rightType, constraint->scope);
asMutable(resultType)->ty.emplace<BoundType>(anyPresent ? builtinTypes->anyType : leftType);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
else if (get<NeverType>(leftType) || get<NeverType>(rightType))
{
unify(leftType, rightType, constraint->scope);
asMutable(resultType)->ty.emplace<BoundType>(builtinTypes->neverType);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
@ -814,14 +814,14 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
{
unify(leftType, rightType, constraint->scope);
asMutable(resultType)->ty.emplace<BoundType>(anyPresent ? builtinTypes->anyType : leftType);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
else if (get<NeverType>(leftType) || get<NeverType>(rightType))
{
unify(leftType, rightType, constraint->scope);
asMutable(resultType)->ty.emplace<BoundType>(builtinTypes->neverType);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
@ -840,14 +840,14 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
if (lt && rt && (lt->isExactlyNumber() || get<AnyType>(lt->tops)) && rt->isExactlyNumber())
{
asMutable(resultType)->ty.emplace<BoundType>(builtinTypes->booleanType);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
if (lt && rt && (lt->isSubtypeOfString() || get<AnyType>(lt->tops)) && rt->isSubtypeOfString())
{
asMutable(resultType)->ty.emplace<BoundType>(builtinTypes->booleanType);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
@ -855,7 +855,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
if (get<NeverType>(leftType) || get<NeverType>(rightType))
{
asMutable(resultType)->ty.emplace<BoundType>(builtinTypes->booleanType);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
@ -867,7 +867,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
case AstExprBinary::Op::CompareEq:
case AstExprBinary::Op::CompareNe:
asMutable(resultType)->ty.emplace<BoundType>(builtinTypes->booleanType);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
// And evalutes to a boolean if the LHS is falsey, and the RHS type if LHS is
// truthy.
@ -876,7 +876,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
TypeId leftFilteredTy = simplifyIntersection(builtinTypes, arena, leftType, builtinTypes->falsyType).result;
asMutable(resultType)->ty.emplace<BoundType>(simplifyUnion(builtinTypes, arena, rightType, leftFilteredTy).result);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
// Or evaluates to the LHS type if the LHS is truthy, and the RHS type if
@ -886,7 +886,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
TypeId leftFilteredTy = simplifyIntersection(builtinTypes, arena, leftType, builtinTypes->truthyType).result;
asMutable(resultType)->ty.emplace<BoundType>(simplifyUnion(builtinTypes, arena, rightType, leftFilteredTy).result);
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
default:
@ -898,7 +898,7 @@ bool ConstraintSolver::tryDispatch(const BinaryConstraint& c, NotNull<const Cons
unify(leftType, errorRecoveryType(), constraint->scope);
unify(rightType, errorRecoveryType(), constraint->scope);
asMutable(resultType)->ty.emplace<BoundType>(errorRecoveryType());
unblock(resultType);
unblock(resultType, constraint->location);
return true;
}
@ -1065,14 +1065,14 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
const PendingExpansionType* petv = get<PendingExpansionType>(follow(c.target));
if (!petv)
{
unblock(c.target);
unblock(c.target, constraint->location);
return true;
}
auto bindResult = [this, &c](TypeId result) {
auto bindResult = [this, &c, constraint](TypeId result) {
LUAU_ASSERT(get<PendingExpansionType>(c.target));
asMutable(c.target)->ty.emplace<BoundType>(result);
unblock(c.target);
unblock(c.target, constraint->location);
};
std::optional<TypeFun> tf = (petv->prefix) ? constraint->scope->lookupImportedType(petv->prefix->value, petv->name.value)
@ -1400,9 +1400,9 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
const auto [changedTypes, changedPacks] = bestOverloadLog->getChanges();
bestOverloadLog->commit();
unblock(changedTypes);
unblock(changedPacks);
unblock(c.result);
unblock(changedTypes, constraint->location);
unblock(changedPacks, constraint->location);
unblock(c.result, constraint->location);
InstantiationQueuer queuer{constraint->scope, constraint->location, this};
queuer.traverse(fn);
@ -1421,7 +1421,7 @@ bool ConstraintSolver::tryDispatch(const PrimitiveTypeConstraint& c, NotNull<con
TypeId bindTo = maybeSingleton(expectedType) ? c.singletonType : c.multitonType;
asMutable(c.resultType)->ty.emplace<BoundType>(bindTo);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
@ -1440,7 +1440,7 @@ bool ConstraintSolver::tryDispatch(const HasPropConstraint& c, NotNull<const Con
TableType& ttv = asMutable(subjectType)->ty.emplace<TableType>(TableState::Free, TypeLevel{}, constraint->scope);
ttv.props[c.prop] = Property{c.resultType};
asMutable(c.resultType)->ty.emplace<FreeType>(constraint->scope);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
@ -1454,7 +1454,7 @@ bool ConstraintSolver::tryDispatch(const HasPropConstraint& c, NotNull<const Con
}
asMutable(c.resultType)->ty.emplace<BoundType>(result.value_or(builtinTypes->anyType));
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
@ -1568,7 +1568,7 @@ bool ConstraintSolver::tryDispatch(const SetPropConstraint& c, NotNull<const Con
if (!isBlocked(c.propType))
unify(c.propType, *existingPropType, constraint->scope);
bind(c.resultType, c.subjectType);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
@ -1593,8 +1593,8 @@ bool ConstraintSolver::tryDispatch(const SetPropConstraint& c, NotNull<const Con
bind(subjectType, ty);
if (follow(c.resultType) != follow(ty))
bind(c.resultType, ty);
unblock(subjectType);
unblock(c.resultType);
unblock(subjectType, constraint->location);
unblock(c.resultType, constraint->location);
return true;
}
else if (auto ttv = getMutable<TableType>(subjectType))
@ -1605,7 +1605,7 @@ bool ConstraintSolver::tryDispatch(const SetPropConstraint& c, NotNull<const Con
ttv->props[c.path[0]] = Property{c.propType};
bind(c.resultType, c.subjectType);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
else if (ttv->state == TableState::Unsealed)
@ -1614,14 +1614,14 @@ bool ConstraintSolver::tryDispatch(const SetPropConstraint& c, NotNull<const Con
updateTheTableType(builtinTypes, NotNull{arena}, subjectType, c.path, c.propType);
bind(c.resultType, c.subjectType);
unblock(subjectType);
unblock(c.resultType);
unblock(subjectType, constraint->location);
unblock(c.resultType, constraint->location);
return true;
}
else
{
bind(c.resultType, subjectType);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
}
@ -1630,7 +1630,7 @@ bool ConstraintSolver::tryDispatch(const SetPropConstraint& c, NotNull<const Con
// Other kinds of types don't change shape when properties are assigned
// to them. (if they allow properties at all!)
bind(c.resultType, subjectType);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
}
@ -1649,8 +1649,8 @@ bool ConstraintSolver::tryDispatch(const SetIndexerConstraint& c, NotNull<const
asMutable(c.resultType)->ty.emplace<BoundType>(subjectType);
asMutable(c.propType)->ty.emplace<FreeType>(scope);
unblock(c.propType);
unblock(c.resultType);
unblock(c.propType, constraint->location);
unblock(c.resultType, constraint->location);
return true;
}
@ -1662,8 +1662,8 @@ bool ConstraintSolver::tryDispatch(const SetIndexerConstraint& c, NotNull<const
unify(c.indexType, tt->indexer->indexType, constraint->scope);
asMutable(c.propType)->ty.emplace<BoundType>(tt->indexer->indexResultType);
asMutable(c.resultType)->ty.emplace<BoundType>(subjectType);
unblock(c.propType);
unblock(c.resultType);
unblock(c.propType, constraint->location);
unblock(c.resultType, constraint->location);
return true;
}
else if (tt->state == TableState::Free || tt->state == TableState::Unsealed)
@ -1675,8 +1675,8 @@ bool ConstraintSolver::tryDispatch(const SetIndexerConstraint& c, NotNull<const
mtt->indexer = TableIndexer{promotedIndexTy, c.propType};
asMutable(c.propType)->ty.emplace<FreeType>(tt->scope);
asMutable(c.resultType)->ty.emplace<BoundType>(subjectType);
unblock(c.propType);
unblock(c.resultType);
unblock(c.propType, constraint->location);
unblock(c.resultType, constraint->location);
return true;
}
// Do not augment sealed or generic tables that lack indexers
@ -1684,8 +1684,8 @@ bool ConstraintSolver::tryDispatch(const SetIndexerConstraint& c, NotNull<const
asMutable(c.propType)->ty.emplace<BoundType>(builtinTypes->errorRecoveryType());
asMutable(c.resultType)->ty.emplace<BoundType>(builtinTypes->errorRecoveryType());
unblock(c.propType);
unblock(c.resultType);
unblock(c.propType, constraint->location);
unblock(c.resultType, constraint->location);
return true;
}
@ -1704,7 +1704,7 @@ bool ConstraintSolver::tryDispatch(const SingletonOrTopTypeConstraint& c, NotNul
else
*asMutable(c.resultType) = BoundType{builtinTypes->anyType};
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
@ -1720,7 +1720,7 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
if (isBlocked(resultPack))
{
asMutable(resultPack)->ty.emplace<BoundTypePack>(sourcePack);
unblock(resultPack);
unblock(resultPack, constraint->location);
return true;
}
@ -1745,7 +1745,7 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
}
else
asMutable(*destIter)->ty.emplace<BoundType>(srcTy);
unblock(*destIter);
unblock(*destIter, constraint->location);
}
else
unify(*destIter, srcTy, constraint->scope);
@ -1763,7 +1763,7 @@ bool ConstraintSolver::tryDispatch(const UnpackConstraint& c, NotNull<const Cons
if (isBlocked(*destIter))
{
asMutable(*destIter)->ty.emplace<BoundType>(builtinTypes->errorRecoveryType());
unblock(*destIter);
unblock(*destIter, constraint->location);
}
++destIter;
@ -1852,7 +1852,7 @@ bool ConstraintSolver::tryDispatch(const RefineConstraint& c, NotNull<const Cons
if (c.mode == RefineConstraint::Intersection && isNegatedAny(c.discriminant))
{
asMutable(c.resultType)->ty.emplace<BoundType>(c.type);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
@ -1880,7 +1880,7 @@ bool ConstraintSolver::tryDispatch(const RefineConstraint& c, NotNull<const Cons
else
asMutable(c.resultType)->ty.emplace<BoundType>(c.discriminant);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
@ -1892,7 +1892,7 @@ bool ConstraintSolver::tryDispatch(const RefineConstraint& c, NotNull<const Cons
asMutable(c.resultType)->ty.emplace<BoundType>(result);
unblock(c.resultType);
unblock(c.resultType, constraint->location);
return true;
}
@ -1904,10 +1904,10 @@ bool ConstraintSolver::tryDispatch(const ReduceConstraint& c, NotNull<const Cons
reduceFamilies(ty, constraint->location, NotNull{arena}, builtinTypes, constraint->scope, normalizer, nullptr, force);
for (TypeId r : result.reducedTypes)
unblock(r);
unblock(r, constraint->location);
for (TypePackId r : result.reducedPacks)
unblock(r);
unblock(r, constraint->location);
if (force)
return true;
@ -1928,10 +1928,10 @@ bool ConstraintSolver::tryDispatch(const ReducePackConstraint& c, NotNull<const
reduceFamilies(tp, constraint->location, NotNull{arena}, builtinTypes, constraint->scope, normalizer, nullptr, force);
for (TypeId r : result.reducedTypes)
unblock(r);
unblock(r, constraint->location);
for (TypePackId r : result.reducedPacks)
unblock(r);
unblock(r, constraint->location);
if (force)
return true;
@ -2374,8 +2374,8 @@ bool ConstraintSolver::tryUnify(NotNull<const Constraint> constraint, TID subTy,
u.log.commit();
unblock(changedTypes);
unblock(changedPacks);
unblock(changedTypes, constraint->location);
unblock(changedPacks, constraint->location);
return true;
}
@ -2509,7 +2509,7 @@ void ConstraintSolver::unblock(NotNull<const Constraint> progressed)
return unblock_(progressed.get());
}
void ConstraintSolver::unblock(TypeId ty)
void ConstraintSolver::unblock(TypeId ty, Location location)
{
DenseHashSet<TypeId> seen{nullptr};
@ -2517,7 +2517,7 @@ void ConstraintSolver::unblock(TypeId ty)
while (true)
{
if (seen.find(progressed))
iceReporter.ice("ConstraintSolver::unblock encountered a self-bound type!");
iceReporter.ice("ConstraintSolver::unblock encountered a self-bound type!", location);
seen.insert(progressed);
if (logger)
@ -2532,7 +2532,7 @@ void ConstraintSolver::unblock(TypeId ty)
}
}
void ConstraintSolver::unblock(TypePackId progressed)
void ConstraintSolver::unblock(TypePackId progressed, Location)
{
if (logger)
logger->popBlock(progressed);
@ -2540,16 +2540,16 @@ void ConstraintSolver::unblock(TypePackId progressed)
return unblock_(progressed);
}
void ConstraintSolver::unblock(const std::vector<TypeId>& types)
void ConstraintSolver::unblock(const std::vector<TypeId>& types, Location location)
{
for (TypeId t : types)
unblock(t);
unblock(t, location);
}
void ConstraintSolver::unblock(const std::vector<TypePackId>& packs)
void ConstraintSolver::unblock(const std::vector<TypePackId>& packs, Location location)
{
for (TypePackId t : packs)
unblock(t);
unblock(t, location);
}
bool ConstraintSolver::isBlocked(TypeId ty)
@ -2586,8 +2586,8 @@ ErrorVec ConstraintSolver::unify(TypeId subType, TypeId superType, NotNull<Scope
u.log.commit();
unblock(changedTypes);
unblock(changedPacks);
unblock(changedTypes, Location{});
unblock(changedPacks, Location{});
return std::move(u.errors);
}
@ -2604,8 +2604,8 @@ ErrorVec ConstraintSolver::unify(TypePackId subPack, TypePackId superPack, NotNu
u.log.commit();
unblock(changedTypes);
unblock(changedPacks);
unblock(changedTypes, Location{});
unblock(changedPacks, Location{});
return std::move(u.errors);
}

View File

@ -35,6 +35,7 @@ LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 100)
LUAU_FASTFLAGVARIABLE(DebugLuauDeferredConstraintResolution, false)
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false)
LUAU_FASTFLAGVARIABLE(DebugLuauReadWriteProperties, false)
LUAU_FASTFLAGVARIABLE(LuauFixBuildQueueExceptionUnwrap, false)
namespace Luau
{
@ -596,6 +597,7 @@ std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptio
sendCycleItemTask();
std::vector<size_t> nextItems;
std::optional<size_t> itemWithException;
while (remaining != 0)
{
@ -603,17 +605,25 @@ std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptio
std::unique_lock guard(mtx);
// If nothing is ready yet, wait
if (readyQueueItems.empty())
{
cv.wait(guard, [&readyQueueItems] {
return !readyQueueItems.empty();
});
}
cv.wait(guard, [&readyQueueItems] {
return !readyQueueItems.empty();
});
// Handle checked items
for (size_t i : readyQueueItems)
{
const BuildQueueItem& item = buildQueueItems[i];
if (FFlag::LuauFixBuildQueueExceptionUnwrap)
{
// If exception was thrown, stop adding new items and wait for processing items to complete
if (item.exception)
itemWithException = i;
if (itemWithException)
break;
}
recordItemResult(item);
// Notify items that were waiting for this dependency
@ -648,7 +658,16 @@ std::vector<ModuleName> Frontend::checkQueuedModules(std::optional<FrontendOptio
// If we aren't done, but don't have anything processing, we hit a cycle
if (remaining != 0 && processing == 0)
{
// We might have stopped because of a pending exception
if (FFlag::LuauFixBuildQueueExceptionUnwrap && itemWithException)
{
recordItemResult(buildQueueItems[*itemWithException]);
break;
}
sendCycleItemTask();
}
}
std::vector<ModuleName> checkedModules;
@ -1104,6 +1123,8 @@ ModulePtr check(const SourceModule& sourceModule, const std::vector<RequireCycle
result->name = sourceModule.name;
result->humanReadableName = sourceModule.humanReadableName;
iceHandler->moduleName = sourceModule.name;
std::unique_ptr<DcrLogger> logger;
if (recordJsonLog)
{
@ -1189,9 +1210,19 @@ ModulePtr Frontend::check(const SourceModule& sourceModule, Mode mode, std::vect
prepareModuleScope(name, scope, forAutocomplete);
};
return Luau::check(sourceModule, requireCycles, builtinTypes, NotNull{&iceHandler},
NotNull{forAutocomplete ? &moduleResolverForAutocomplete : &moduleResolver}, NotNull{fileResolver},
environmentScope ? *environmentScope : globals.globalScope, prepareModuleScopeWrap, options, recordJsonLog);
try
{
return Luau::check(sourceModule, requireCycles, builtinTypes, NotNull{&iceHandler},
NotNull{forAutocomplete ? &moduleResolverForAutocomplete : &moduleResolver}, NotNull{fileResolver},
environmentScope ? *environmentScope : globals.globalScope, prepareModuleScopeWrap, options, recordJsonLog);
}
catch (const InternalCompilerError& err)
{
InternalCompilerError augmented = err.location.has_value()
? InternalCompilerError{err.message, sourceModule.humanReadableName, *err.location}
: InternalCompilerError{err.message, sourceModule.humanReadableName};
throw augmented;
}
}
else
{

View File

@ -2117,15 +2117,15 @@ std::optional<TypeId> Normalizer::intersectionOfTables(TypeId here, TypeId there
TypeId hmtable = nullptr;
if (const MetatableType* hmtv = get<MetatableType>(here))
{
htable = hmtv->table;
hmtable = hmtv->metatable;
htable = follow(hmtv->table);
hmtable = follow(hmtv->metatable);
}
TypeId ttable = there;
TypeId tmtable = nullptr;
if (const MetatableType* tmtv = get<MetatableType>(there))
{
ttable = tmtv->table;
tmtable = tmtv->metatable;
ttable = follow(tmtv->table);
tmtable = follow(tmtv->metatable);
}
const TableType* httv = get<TableType>(htable);

View File

@ -6,6 +6,7 @@
#include "Luau/ToString.h"
#include "Luau/TypeArena.h"
#include "Luau/Normalize.h" // TypeIds
#include <algorithm>
LUAU_FASTINT(LuauTypeReductionRecursionLimit)
@ -236,6 +237,17 @@ Relation relateTables(TypeId left, TypeId right)
NotNull<const TableType> leftTable{get<TableType>(left)};
NotNull<const TableType> rightTable{get<TableType>(right)};
LUAU_ASSERT(1 == rightTable->props.size());
// Disjoint props have nothing in common
// t1 with props p1's cannot appear in t2 and t2 with props p2's cannot appear in t1
bool foundPropFromLeftInRight = std::any_of(begin(leftTable->props), end(leftTable->props), [&](auto prop) {
return rightTable->props.find(prop.first) != end(rightTable->props);
});
bool foundPropFromRightInLeft = std::any_of(begin(rightTable->props), end(rightTable->props), [&](auto prop) {
return leftTable->props.find(prop.first) != end(leftTable->props);
});
if (!(foundPropFromLeftInRight || foundPropFromRightInLeft) && leftTable->props.size() >= 1 && rightTable->props.size() >= 1)
return Relation::Disjoint;
const auto [propName, rightProp] = *begin(rightTable->props);

View File

@ -111,11 +111,14 @@ static TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log, bool a
else if constexpr (std::is_same_v<T, GenericType>)
return dest.addType(a);
else if constexpr (std::is_same_v<T, BlockedType>)
return ty;
return dest.addType(a);
else if constexpr (std::is_same_v<T, PrimitiveType>)
return ty;
else if constexpr (std::is_same_v<T, PendingExpansionType>)
return ty;
{
PendingExpansionType clone = PendingExpansionType{a.prefix, a.name, a.typeArguments, a.packArguments};
return dest.addType(std::move(clone));
}
else if constexpr (std::is_same_v<T, AnyType>)
return ty;
else if constexpr (std::is_same_v<T, ErrorType>)

View File

@ -6,7 +6,6 @@
#include "Luau/CodeGen.h"
#include "Luau/Compiler.h"
#include "Luau/BytecodeBuilder.h"
#include "Luau/Parser.h"
#include "Luau/TimeTrace.h"
@ -40,27 +39,6 @@
LUAU_FASTFLAG(DebugLuauTimeTracing)
enum class CliMode
{
Unknown,
Repl,
Compile,
RunSourceFiles
};
enum class CompileFormat
{
Text,
Binary,
Remarks,
Codegen, // Prints annotated native code including IR and assembly
CodegenAsm, // Prints annotated native code assembly
CodegenIr, // Prints annotated native code IR
CodegenVerbose, // Prints annotated native code including IR, assembly and outlined code
CodegenNull,
Null
};
constexpr int MaxTraversalLimit = 50;
static bool codegen = false;
@ -668,178 +646,11 @@ static bool runFile(const char* name, lua_State* GL, bool repl)
return status == 0;
}
static void report(const char* name, const Luau::Location& location, const char* type, const char* message)
{
fprintf(stderr, "%s(%d,%d): %s: %s\n", name, location.begin.line + 1, location.begin.column + 1, type, message);
}
static void reportError(const char* name, const Luau::ParseError& error)
{
report(name, error.getLocation(), "SyntaxError", error.what());
}
static void reportError(const char* name, const Luau::CompileError& error)
{
report(name, error.getLocation(), "CompileError", error.what());
}
static std::string getCodegenAssembly(const char* name, const std::string& bytecode, Luau::CodeGen::AssemblyOptions options)
{
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
lua_State* L = globalState.get();
if (luau_load(L, name, bytecode.data(), bytecode.size(), 0) == 0)
return Luau::CodeGen::getAssembly(L, -1, options);
fprintf(stderr, "Error loading bytecode %s\n", name);
return "";
}
static void annotateInstruction(void* context, std::string& text, int fid, int instpos)
{
Luau::BytecodeBuilder& bcb = *(Luau::BytecodeBuilder*)context;
bcb.annotateInstruction(text, fid, instpos);
}
struct CompileStats
{
size_t lines;
size_t bytecode;
size_t codegen;
double readTime;
double miscTime;
double parseTime;
double compileTime;
double codegenTime;
};
static double recordDeltaTime(double& timer)
{
double now = Luau::TimeTrace::getClock();
double delta = now - timer;
timer = now;
return delta;
}
static bool compileFile(const char* name, CompileFormat format, CompileStats& stats)
{
double currts = Luau::TimeTrace::getClock();
std::optional<std::string> source = readFile(name);
if (!source)
{
fprintf(stderr, "Error opening %s\n", name);
return false;
}
stats.readTime += recordDeltaTime(currts);
// NOTE: Normally, you should use Luau::compile or luau_compile (see lua_require as an example)
// This function is much more complicated because it supports many output human-readable formats through internal interfaces
try
{
Luau::BytecodeBuilder bcb;
Luau::CodeGen::AssemblyOptions options;
options.outputBinary = format == CompileFormat::CodegenNull;
if (!options.outputBinary)
{
options.includeAssembly = format != CompileFormat::CodegenIr;
options.includeIr = format != CompileFormat::CodegenAsm;
options.includeOutlinedCode = format == CompileFormat::CodegenVerbose;
}
options.annotator = annotateInstruction;
options.annotatorContext = &bcb;
if (format == CompileFormat::Text)
{
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
Luau::BytecodeBuilder::Dump_Remarks);
bcb.setDumpSource(*source);
}
else if (format == CompileFormat::Remarks)
{
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Remarks);
bcb.setDumpSource(*source);
}
else if (format == CompileFormat::Codegen || format == CompileFormat::CodegenAsm || format == CompileFormat::CodegenIr ||
format == CompileFormat::CodegenVerbose)
{
bcb.setDumpFlags(Luau::BytecodeBuilder::Dump_Code | Luau::BytecodeBuilder::Dump_Source | Luau::BytecodeBuilder::Dump_Locals |
Luau::BytecodeBuilder::Dump_Remarks);
bcb.setDumpSource(*source);
}
stats.miscTime += recordDeltaTime(currts);
Luau::Allocator allocator;
Luau::AstNameTable names(allocator);
Luau::ParseResult result = Luau::Parser::parse(source->c_str(), source->size(), names, allocator);
if (!result.errors.empty())
throw Luau::ParseErrors(result.errors);
stats.lines += result.lines;
stats.parseTime += recordDeltaTime(currts);
Luau::compileOrThrow(bcb, result, names, copts());
stats.bytecode += bcb.getBytecode().size();
stats.compileTime += recordDeltaTime(currts);
switch (format)
{
case CompileFormat::Text:
printf("%s", bcb.dumpEverything().c_str());
break;
case CompileFormat::Remarks:
printf("%s", bcb.dumpSourceRemarks().c_str());
break;
case CompileFormat::Binary:
fwrite(bcb.getBytecode().data(), 1, bcb.getBytecode().size(), stdout);
break;
case CompileFormat::Codegen:
case CompileFormat::CodegenAsm:
case CompileFormat::CodegenIr:
case CompileFormat::CodegenVerbose:
printf("%s", getCodegenAssembly(name, bcb.getBytecode(), options).c_str());
break;
case CompileFormat::CodegenNull:
stats.codegen += getCodegenAssembly(name, bcb.getBytecode(), options).size();
stats.codegenTime += recordDeltaTime(currts);
break;
case CompileFormat::Null:
break;
}
return true;
}
catch (Luau::ParseErrors& e)
{
for (auto& error : e.getErrors())
reportError(name, error);
return false;
}
catch (Luau::CompileError& e)
{
reportError(name, e);
return false;
}
}
static void displayHelp(const char* argv0)
{
printf("Usage: %s [--mode] [options] [file list]\n", argv0);
printf("Usage: %s [options] [file list]\n", argv0);
printf("\n");
printf("When mode and file list are omitted, an interactive REPL is started instead.\n");
printf("\n");
printf("Available modes:\n");
printf(" omitted: compile and run input files one by one\n");
printf(" --compile[=format]: compile input files and output resulting bytecode/assembly (binary, text, remarks, codegen)\n");
printf("When file list is omitted, an interactive REPL is started instead.\n");
printf("\n");
printf("Available options:\n");
printf(" --coverage: collect code coverage while running the code and output results to coverage.out\n");
@ -864,67 +675,12 @@ int replMain(int argc, char** argv)
setLuauFlagsDefault();
CliMode mode = CliMode::Unknown;
CompileFormat compileFormat{};
int profile = 0;
bool coverage = false;
bool interactive = false;
bool codegenPerf = false;
// Set the mode if the user has explicitly specified one.
int argStart = 1;
if (argc >= 2 && strncmp(argv[1], "--compile", strlen("--compile")) == 0)
{
argStart++;
mode = CliMode::Compile;
if (strcmp(argv[1], "--compile") == 0)
{
compileFormat = CompileFormat::Text;
}
else if (strcmp(argv[1], "--compile=binary") == 0)
{
compileFormat = CompileFormat::Binary;
}
else if (strcmp(argv[1], "--compile=text") == 0)
{
compileFormat = CompileFormat::Text;
}
else if (strcmp(argv[1], "--compile=remarks") == 0)
{
compileFormat = CompileFormat::Remarks;
}
else if (strcmp(argv[1], "--compile=codegen") == 0)
{
compileFormat = CompileFormat::Codegen;
}
else if (strcmp(argv[1], "--compile=codegenasm") == 0)
{
compileFormat = CompileFormat::CodegenAsm;
}
else if (strcmp(argv[1], "--compile=codegenir") == 0)
{
compileFormat = CompileFormat::CodegenIr;
}
else if (strcmp(argv[1], "--compile=codegenverbose") == 0)
{
compileFormat = CompileFormat::CodegenVerbose;
}
else if (strcmp(argv[1], "--compile=codegennull") == 0)
{
compileFormat = CompileFormat::CodegenNull;
}
else if (strcmp(argv[1], "--compile=null") == 0)
{
compileFormat = CompileFormat::Null;
}
else
{
fprintf(stderr, "Error: Unrecognized value for '--compile' specified.\n");
return 1;
}
}
for (int i = argStart; i < argc; i++)
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0)
{
@ -1026,50 +782,20 @@ int replMain(int argc, char** argv)
#endif
}
const std::vector<std::string> files = getSourceFiles(argc, argv);
if (mode == CliMode::Unknown)
{
mode = files.empty() ? CliMode::Repl : CliMode::RunSourceFiles;
}
if (mode != CliMode::Compile && codegen && !Luau::CodeGen::isSupported())
if (codegen && !Luau::CodeGen::isSupported())
{
fprintf(stderr, "Cannot enable --codegen, native code generation is not supported in current configuration\n");
return 1;
}
switch (mode)
{
case CliMode::Compile:
{
#ifdef _WIN32
if (compileFormat == CompileFormat::Binary)
_setmode(_fileno(stdout), _O_BINARY);
#endif
const std::vector<std::string> files = getSourceFiles(argc, argv);
CompileStats stats = {};
int failed = 0;
for (const std::string& path : files)
failed += !compileFile(path.c_str(), compileFormat, stats);
if (compileFormat == CompileFormat::Null)
printf("Compiled %d KLOC into %d KB bytecode (read %.2fs, parse %.2fs, compile %.2fs)\n", int(stats.lines / 1000),
int(stats.bytecode / 1024), stats.readTime, stats.parseTime, stats.compileTime);
else if (compileFormat == CompileFormat::CodegenNull)
printf("Compiled %d KLOC into %d KB bytecode => %d KB native code (%.2fx) (read %.2fs, parse %.2fs, compile %.2fs, codegen %.2fs)\n",
int(stats.lines / 1000), int(stats.bytecode / 1024), int(stats.codegen / 1024),
stats.bytecode == 0 ? 0.0 : double(stats.codegen) / double(stats.bytecode), stats.readTime, stats.parseTime, stats.compileTime,
stats.codegenTime);
return failed ? 1 : 0;
}
case CliMode::Repl:
if (files.empty())
{
runRepl();
return 0;
}
case CliMode::RunSourceFiles:
else
{
std::unique_ptr<lua_State, void (*)(lua_State*)> globalState(luaL_newstate(), lua_close);
lua_State* L = globalState.get();
@ -1101,9 +827,4 @@ int replMain(int argc, char** argv)
return failed ? 1 : 0;
}
case CliMode::Unknown:
default:
LUAU_ASSERT(!"Unhandled cli mode.");
return 1;
}
}

View File

@ -98,6 +98,8 @@ public:
void call(Label& label);
void call(OperandX64 op);
void lea(RegisterX64 lhs, Label& label);
void int3();
void ud2();
@ -243,6 +245,7 @@ private:
LUAU_NOINLINE void log(const char* opcode, OperandX64 op1, OperandX64 op2, OperandX64 op3, OperandX64 op4);
LUAU_NOINLINE void log(Label label);
LUAU_NOINLINE void log(const char* opcode, Label label);
LUAU_NOINLINE void log(const char* opcode, RegisterX64 reg, Label label);
void log(OperandX64 op);
const char* getSizeName(SizeX64 size) const;

View File

@ -801,6 +801,8 @@ struct IrBlock
uint32_t start = ~0u;
uint32_t finish = ~0u;
uint32_t sortkey = ~0u;
Label label;
};

View File

@ -38,6 +38,8 @@ std::string toString(const IrFunction& function, bool includeUseInfo);
std::string dump(const IrFunction& function);
std::string toDot(const IrFunction& function, bool includeInst);
std::string toDotCfg(const IrFunction& function);
std::string toDotDjGraph(const IrFunction& function);
std::string dumpDot(const IrFunction& function, bool includeInst);

View File

@ -463,6 +463,20 @@ void AssemblyBuilderX64::call(OperandX64 op)
commit();
}
void AssemblyBuilderX64::lea(RegisterX64 lhs, Label& label)
{
LUAU_ASSERT(lhs.size == SizeX64::qword);
placeBinaryRegAndRegMem(lhs, OperandX64(SizeX64::qword, noreg, 1, rip, 0), 0x8d, 0x8d);
codePos -= 4;
placeLabel(label);
commit();
if (logText)
log("lea", lhs, label);
}
void AssemblyBuilderX64::int3()
{
if (logText)
@ -1415,7 +1429,7 @@ void AssemblyBuilderX64::commit()
{
LUAU_ASSERT(codePos <= codeEnd);
if (codeEnd - codePos < kMaxInstructionLength)
if (unsigned(codeEnd - codePos) < kMaxInstructionLength)
extend();
}
@ -1501,6 +1515,14 @@ void AssemblyBuilderX64::log(const char* opcode, Label label)
logAppend(" %-12s.L%d\n", opcode, label.id);
}
void AssemblyBuilderX64::log(const char* opcode, RegisterX64 reg, Label label)
{
logAppend(" %-12s", opcode);
log(reg);
text.append(",");
logAppend(".L%d\n", label.id);
}
void AssemblyBuilderX64::log(OperandX64 op)
{
switch (op.cat)

View File

@ -56,8 +56,10 @@ static void makePagesExecutable(uint8_t* mem, size_t size)
static void flushInstructionCache(uint8_t* mem, size_t size)
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
if (FlushInstructionCache(GetCurrentProcess(), mem, size) == 0)
LUAU_ASSERT(!"Failed to flush instruction cache");
#endif
}
#else
static uint8_t* allocatePages(size_t size)

View File

@ -125,7 +125,7 @@ static bool lowerImpl(AssemblyBuilder& build, IrLowering& lowering, IrFunction&
return (a.kind == IrBlockKind::Fallback) < (b.kind == IrBlockKind::Fallback);
// Try to order by instruction order
return a.start < b.start;
return a.sortkey < b.sortkey;
});
// For each IR instruction that begins a bytecode instruction, which bytecode instruction is it?
@ -234,6 +234,8 @@ static bool lowerImpl(AssemblyBuilder& build, IrLowering& lowering, IrFunction&
build.setLabel(abandoned.label);
}
lowering.finishFunction();
return false;
}
}
@ -244,7 +246,15 @@ static bool lowerImpl(AssemblyBuilder& build, IrLowering& lowering, IrFunction&
build.logAppend("#\n");
}
if (outputEnabled && !options.includeOutlinedCode && seenFallback)
if (!seenFallback)
{
textSize = build.text.length();
codeSize = build.getCodeSize();
}
lowering.finishFunction();
if (outputEnabled && !options.includeOutlinedCode && textSize < build.text.size())
{
build.text.resize(textSize);
@ -594,6 +604,12 @@ std::string getAssembly(lua_State* L, int idx, AssemblyOptions options)
X64::assembleHelpers(build, helpers);
#endif
if (!options.includeOutlinedCode && options.includeAssembly)
{
build.text.clear();
build.logAppend("; skipping %u bytes of outlined helpers\n", unsigned(build.getCodeSize() * sizeof(build.code[0])));
}
for (Proto* p : protos)
if (p)
if (std::optional<NativeProto> np = assembleFunction(build, data, helpers, p, options))

View File

@ -288,27 +288,27 @@ void assembleHelpers(AssemblyBuilderA64& build, ModuleHelpers& helpers)
{
if (build.logText)
build.logAppend("; exitContinueVm\n");
helpers.exitContinueVm = build.setLabel();
build.setLabel(helpers.exitContinueVm);
emitExit(build, /* continueInVm */ true);
if (build.logText)
build.logAppend("; exitNoContinueVm\n");
helpers.exitNoContinueVm = build.setLabel();
build.setLabel(helpers.exitNoContinueVm);
emitExit(build, /* continueInVm */ false);
if (build.logText)
build.logAppend("; reentry\n");
helpers.reentry = build.setLabel();
build.setLabel(helpers.reentry);
emitReentry(build, helpers);
if (build.logText)
build.