From fe7621ee8cd84b8e2774717cf9de79a9c47cd0f7 Mon Sep 17 00:00:00 2001 From: Andy Friesen Date: Fri, 21 Apr 2023 15:14:26 -0700 Subject: [PATCH] Sync to upstream/release/573 (#903) * Work toward affording parallel type checking * The interface to `LazyType` has changed: * `LazyType` now takes a second callback that is passed the `LazyType&` itself. This new callback is responsible for populating the field `TypeId LazyType::unwrapped`. Multithreaded implementations should acquire a lock in this callback. * Modules now retain their `humanReadableNames`. This reduces the number of cases where type checking has to call back to a `ModuleResolver`. * https://github.com/Roblox/luau/pull/902 * Add timing info to the Luau REPL compilation output We've also fixed some bugs and crashes in the new solver as we march toward readiness. * Thread ICEs (Internal Compiler Errors) back to the Frontend properly * Refinements are no longer applied to lvalues * More miscellaneous stability improvements Lots of activity in the new JIT engine: * Implement register spilling/restore for A64 * Correct Luau IR value restore location tracking * Fixed use-after-free in x86 register allocator spill restore * Use btz for bit tests * Finish branch assembly support for A64 * Codesize and performance improvements for A64 * The bit32 library has been implemented for arm and x64 --------- Co-authored-by: Arseny Kapoulkine Co-authored-by: Vyacheslav Egorov --- .../include/Luau/ConstraintGraphBuilder.h | 6 +- Analysis/include/Luau/ConstraintSolver.h | 2 +- Analysis/include/Luau/Frontend.h | 9 + Analysis/include/Luau/Module.h | 7 +- Analysis/include/Luau/Type.h | 48 +- Analysis/include/Luau/TypeChecker2.h | 2 +- Analysis/include/Luau/TypeInfer.h | 1 - Analysis/include/Luau/VisitType.h | 10 +- Analysis/src/BuiltinDefinitions.cpp | 2 +- Analysis/src/Clone.cpp | 9 +- Analysis/src/ConstraintGraphBuilder.cpp | 17 +- Analysis/src/ConstraintSolver.cpp | 13 +- Analysis/src/Error.cpp | 6 +- Analysis/src/Frontend.cpp | 96 ++-- Analysis/src/Substitution.cpp | 2 +- Analysis/src/ToString.cpp | 11 +- Analysis/src/Type.cpp | 53 +- Analysis/src/TypeAttach.cpp | 3 + Analysis/src/TypeChecker2.cpp | 21 +- Analysis/src/TypeInfer.cpp | 40 +- Analysis/src/TypeReduction.cpp | 2 +- CLI/Repl.cpp | 10 +- CodeGen/include/Luau/AssemblyBuilderA64.h | 67 ++- CodeGen/include/Luau/AssemblyBuilderX64.h | 5 + CodeGen/include/Luau/ConditionA64.h | 4 +- CodeGen/include/Luau/IrData.h | 64 ++- CodeGen/include/Luau/IrUtils.h | 21 +- CodeGen/include/Luau/OptimizeConstProp.h | 1 + CodeGen/src/AssemblyBuilderA64.cpp | 305 +++++++--- CodeGen/src/AssemblyBuilderX64.cpp | 38 ++ CodeGen/src/CodeGen.cpp | 13 +- CodeGen/src/CodeGenUtils.cpp | 4 +- CodeGen/src/CodeGenUtils.h | 2 +- CodeGen/src/EmitBuiltinsX64.cpp | 123 ---- CodeGen/src/EmitCommonA64.h | 6 +- CodeGen/src/IrBuilder.cpp | 3 +- CodeGen/src/IrDump.cpp | 34 ++ CodeGen/src/IrLoweringA64.cpp | 538 ++++++++++++------ CodeGen/src/IrLoweringA64.h | 8 +- CodeGen/src/IrLoweringX64.cpp | 234 +++++++- CodeGen/src/IrLoweringX64.h | 6 + CodeGen/src/IrRegAllocA64.cpp | 223 +++++++- CodeGen/src/IrRegAllocA64.h | 38 +- CodeGen/src/IrRegAllocX64.cpp | 67 +-- CodeGen/src/IrTranslateBuiltins.cpp | 428 +++++++++++++- CodeGen/src/IrTranslation.cpp | 6 + CodeGen/src/IrUtils.cpp | 81 ++- CodeGen/src/IrValueLocationTracking.cpp | 223 ++++++++ CodeGen/src/IrValueLocationTracking.h | 38 ++ CodeGen/src/NativeState.h | 2 +- CodeGen/src/OptimizeConstProp.cpp | 55 +- CodeGen/src/UnwindBuilderDwarf2.cpp | 4 +- Sources.cmake | 3 + tests/AssemblyBuilderA64.test.cpp | 55 +- tests/AssemblyBuilderX64.test.cpp | 6 + tests/Conformance.test.cpp | 5 + tests/ConstraintGraphBuilderFixture.cpp | 6 +- tests/Frontend.test.cpp | 12 +- tests/IrBuilder.test.cpp | 3 + tests/IrRegAllocX64.test.cpp | 58 ++ tests/Module.test.cpp | 6 +- tests/TypeInfer.modules.test.cpp | 10 +- tests/TypeInfer.provisional.test.cpp | 8 +- tests/TypeInfer.test.cpp | 9 +- tests/TypeVar.test.cpp | 19 + tests/conformance/native.lua | 17 + tools/codegenstat.py | 58 ++ tools/faillist.txt | 2 + tools/natvis/CodeGen.natvis | 9 + 69 files changed, 2657 insertions(+), 640 deletions(-) create mode 100644 CodeGen/src/IrValueLocationTracking.cpp create mode 100644 CodeGen/src/IrValueLocationTracking.h create mode 100644 tests/IrRegAllocX64.test.cpp create mode 100644 tests/conformance/native.lua create mode 100644 tools/codegenstat.py diff --git a/Analysis/include/Luau/ConstraintGraphBuilder.h b/Analysis/include/Luau/ConstraintGraphBuilder.h index 20447048..cbf679cc 100644 --- a/Analysis/include/Luau/ConstraintGraphBuilder.h +++ b/Analysis/include/Luau/ConstraintGraphBuilder.h @@ -60,7 +60,6 @@ struct ConstraintGraphBuilder // define the scope hierarchy. std::vector> scopes; - ModuleName moduleName; ModulePtr module; NotNull builtinTypes; const NotNull arena; @@ -94,9 +93,8 @@ struct ConstraintGraphBuilder ScopePtr globalScope; DcrLogger* logger; - ConstraintGraphBuilder(const ModuleName& moduleName, ModulePtr module, TypeArena* arena, NotNull moduleResolver, - NotNull builtinTypes, NotNull ice, const ScopePtr& globalScope, DcrLogger* logger, - NotNull dfg); + ConstraintGraphBuilder(ModulePtr module, TypeArena* arena, NotNull moduleResolver, NotNull builtinTypes, + NotNull ice, const ScopePtr& globalScope, DcrLogger* logger, NotNull dfg); /** * Fabricates a new free type belonging to a given scope. diff --git a/Analysis/include/Luau/ConstraintSolver.h b/Analysis/include/Luau/ConstraintSolver.h index 2feee236..6888e99c 100644 --- a/Analysis/include/Luau/ConstraintSolver.h +++ b/Analysis/include/Luau/ConstraintSolver.h @@ -49,7 +49,7 @@ struct HashInstantiationSignature struct ConstraintSolver { - TypeArena* arena; + NotNull arena; NotNull builtinTypes; InternalErrorReporter iceReporter; NotNull normalizer; diff --git a/Analysis/include/Luau/Frontend.h b/Analysis/include/Luau/Frontend.h index 3f41c145..856c5daf 100644 --- a/Analysis/include/Luau/Frontend.h +++ b/Analysis/include/Luau/Frontend.h @@ -8,6 +8,8 @@ #include "Luau/Scope.h" #include "Luau/TypeInfer.h" #include "Luau/Variant.h" + +#include #include #include #include @@ -67,6 +69,7 @@ struct SourceNode } ModuleName name; + std::string humanReadableName; std::unordered_set requireSet; std::vector> requireLocations; bool dirtySourceModule = true; @@ -114,7 +117,13 @@ struct FrontendModuleResolver : ModuleResolver std::optional resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) override; std::string getHumanReadableModuleName(const ModuleName& moduleName) const override; + void setModule(const ModuleName& moduleName, ModulePtr module); + void clearModules(); + +private: Frontend* frontend; + + mutable std::mutex moduleMutex; std::unordered_map modules; }; diff --git a/Analysis/include/Luau/Module.h b/Analysis/include/Luau/Module.h index 1bca7636..b9be8205 100644 --- a/Analysis/include/Luau/Module.h +++ b/Analysis/include/Luau/Module.h @@ -28,7 +28,9 @@ class AstTypePack; /// Root of the AST of a parsed source file struct SourceModule { - ModuleName name; // DataModel path if possible. Filename if not. + ModuleName name; // Module identifier or a filename + std::string humanReadableName; + SourceCode::Type type = SourceCode::None; std::optional environmentName; bool cyclic = false; @@ -63,6 +65,9 @@ struct Module { ~Module(); + ModuleName name; + std::string humanReadableName; + TypeArena interfaceTypes; TypeArena internalTypes; diff --git a/Analysis/include/Luau/Type.h b/Analysis/include/Luau/Type.h index b9544a11..24fb7db0 100644 --- a/Analysis/include/Luau/Type.h +++ b/Analysis/include/Luau/Type.h @@ -10,6 +10,7 @@ #include "Luau/Unifiable.h" #include "Luau/Variant.h" +#include #include #include #include @@ -550,7 +551,50 @@ struct IntersectionType struct LazyType { - std::function thunk; + LazyType() = default; + LazyType(std::function thunk_DEPRECATED, std::function unwrap) + : thunk_DEPRECATED(thunk_DEPRECATED) + , unwrap(unwrap) + { + } + + // std::atomic is sad and requires a manual copy + LazyType(const LazyType& rhs) + : thunk_DEPRECATED(rhs.thunk_DEPRECATED) + , unwrap(rhs.unwrap) + , unwrapped(rhs.unwrapped.load()) + { + } + + LazyType(LazyType&& rhs) noexcept + : thunk_DEPRECATED(std::move(rhs.thunk_DEPRECATED)) + , unwrap(std::move(rhs.unwrap)) + , unwrapped(rhs.unwrapped.load()) + { + } + + LazyType& operator=(const LazyType& rhs) + { + thunk_DEPRECATED = rhs.thunk_DEPRECATED; + unwrap = rhs.unwrap; + unwrapped = rhs.unwrapped.load(); + + return *this; + } + + LazyType& operator=(LazyType&& rhs) noexcept + { + thunk_DEPRECATED = std::move(rhs.thunk_DEPRECATED); + unwrap = std::move(rhs.unwrap); + unwrapped = rhs.unwrapped.load(); + + return *this; + } + + std::function thunk_DEPRECATED; + + std::function unwrap; + std::atomic unwrapped = nullptr; }; struct UnknownType @@ -798,7 +842,7 @@ struct TypeIterator TypeIterator operator++(int) { TypeIterator copy = *this; - ++copy; + ++*this; return copy; } diff --git a/Analysis/include/Luau/TypeChecker2.h b/Analysis/include/Luau/TypeChecker2.h index 6045aecf..def00a44 100644 --- a/Analysis/include/Luau/TypeChecker2.h +++ b/Analysis/include/Luau/TypeChecker2.h @@ -12,6 +12,6 @@ namespace Luau struct DcrLogger; struct BuiltinTypes; -void check(NotNull builtinTypes, DcrLogger* logger, const SourceModule& sourceModule, Module* module); +void check(NotNull builtinTypes, NotNull sharedState, DcrLogger* logger, const SourceModule& sourceModule, Module* module); } // namespace Luau diff --git a/Analysis/include/Luau/TypeInfer.h b/Analysis/include/Luau/TypeInfer.h index 7dae79c3..b5db3f58 100644 --- a/Analysis/include/Luau/TypeInfer.h +++ b/Analysis/include/Luau/TypeInfer.h @@ -372,7 +372,6 @@ public: ModuleResolver* resolver; ModulePtr currentModule; - ModuleName currentModuleName; std::function prepareModuleScope; NotNull builtinTypes; diff --git a/Analysis/include/Luau/VisitType.h b/Analysis/include/Luau/VisitType.h index 95b2b050..c7dcdcc1 100644 --- a/Analysis/include/Luau/VisitType.h +++ b/Analysis/include/Luau/VisitType.h @@ -9,6 +9,7 @@ #include "Luau/Type.h" LUAU_FASTINT(LuauVisitRecursionLimit) +LUAU_FASTFLAG(LuauBoundLazyTypes) namespace Luau { @@ -291,9 +292,14 @@ struct GenericTypeVisitor traverse(partTy); } } - else if (get(ty)) + else if (auto ltv = get(ty)) { - // Visiting into LazyType may necessarily cause infinite expansion, so we don't do that on purpose. + if (FFlag::LuauBoundLazyTypes) + { + if (TypeId unwrapped = ltv->unwrapped) + traverse(unwrapped); + } + // Visiting into LazyType that hasn't been unwrapped may necessarily cause infinite expansion, so we don't do that on purpose. // Asserting also makes no sense, because the type _will_ happen here, most likely as a property of some ClassType // that doesn't need to be expanded. } diff --git a/Analysis/src/BuiltinDefinitions.cpp b/Analysis/src/BuiltinDefinitions.cpp index 7ed92fb4..8988b332 100644 --- a/Analysis/src/BuiltinDefinitions.cpp +++ b/Analysis/src/BuiltinDefinitions.cpp @@ -606,7 +606,7 @@ static std::optional> magicFunctionRequire( if (!checkRequirePath(typechecker, expr.args.data[0])) return std::nullopt; - if (auto moduleInfo = typechecker.resolver->resolveModuleInfo(typechecker.currentModuleName, expr)) + if (auto moduleInfo = typechecker.resolver->resolveModuleInfo(typechecker.currentModule->name, expr)) return WithPredicate{arena.addTypePack({typechecker.checkRequire(scope, *moduleInfo, expr.location)})}; return std::nullopt; diff --git a/Analysis/src/Clone.cpp b/Analysis/src/Clone.cpp index ac73622d..f5102654 100644 --- a/Analysis/src/Clone.cpp +++ b/Analysis/src/Clone.cpp @@ -325,7 +325,14 @@ void TypeCloner::operator()(const IntersectionType& t) void TypeCloner::operator()(const LazyType& t) { - defaultClone(t); + if (TypeId unwrapped = t.unwrapped.load()) + { + seenTypes[typeId] = clone(unwrapped, dest, cloneState); + } + else + { + defaultClone(t); + } } void TypeCloner::operator()(const UnknownType& t) diff --git a/Analysis/src/ConstraintGraphBuilder.cpp b/Analysis/src/ConstraintGraphBuilder.cpp index 474d3923..ad7cff9f 100644 --- a/Analysis/src/ConstraintGraphBuilder.cpp +++ b/Analysis/src/ConstraintGraphBuilder.cpp @@ -133,11 +133,10 @@ void forEachConstraint(const Checkpoint& start, const Checkpoint& end, const Con } // namespace -ConstraintGraphBuilder::ConstraintGraphBuilder(const ModuleName& moduleName, ModulePtr module, TypeArena* arena, - NotNull moduleResolver, NotNull builtinTypes, NotNull ice, const ScopePtr& globalScope, - DcrLogger* logger, NotNull dfg) - : moduleName(moduleName) - , module(module) +ConstraintGraphBuilder::ConstraintGraphBuilder(ModulePtr module, TypeArena* arena, NotNull moduleResolver, + NotNull builtinTypes, NotNull ice, const ScopePtr& globalScope, DcrLogger* logger, + NotNull dfg) + : module(module) , builtinTypes(builtinTypes) , arena(arena) , rootScope(nullptr) @@ -599,7 +598,7 @@ ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* l { AstExpr* require = *maybeRequire; - if (auto moduleInfo = moduleResolver->resolveModuleInfo(moduleName, *require)) + if (auto moduleInfo = moduleResolver->resolveModuleInfo(module->name, *require)) { const Name name{local->vars.data[i]->name.value}; @@ -1043,7 +1042,7 @@ ControlFlow ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareC Name className(declaredClass->name.value); - TypeId classTy = arena->addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, moduleName)); + TypeId classTy = arena->addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, module->name)); ClassType* ctv = getMutable(classTy); TypeId metaTy = arena->addType(TableType{TableState::Sealed, scope->level, scope.get()}); @@ -2609,7 +2608,7 @@ Inference ConstraintGraphBuilder::flattenPack(const ScopePtr& scope, Location lo void ConstraintGraphBuilder::reportError(Location location, TypeErrorData err) { - errors.push_back(TypeError{location, moduleName, std::move(err)}); + errors.push_back(TypeError{location, module->name, std::move(err)}); if (logger) logger->captureGenerationError(errors.back()); @@ -2617,7 +2616,7 @@ void ConstraintGraphBuilder::reportError(Location location, TypeErrorData err) void ConstraintGraphBuilder::reportCodeTooComplex(Location location) { - errors.push_back(TypeError{location, moduleName, CodeTooComplex{}}); + errors.push_back(TypeError{location, module->name, CodeTooComplex{}}); if (logger) logger->captureGenerationError(errors.back()); diff --git a/Analysis/src/ConstraintSolver.cpp b/Analysis/src/ConstraintSolver.cpp index 0fc32c33..558ad2d5 100644 --- a/Analysis/src/ConstraintSolver.cpp +++ b/Analysis/src/ConstraintSolver.cpp @@ -18,6 +18,7 @@ #include "Luau/VisitType.h" LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false); +LUAU_FASTFLAG(LuauRequirePathTrueModuleName) namespace Luau { @@ -1989,7 +1990,7 @@ static TypePackId getErrorType(NotNull builtinTypes, TypePackId) template bool ConstraintSolver::tryUnify(NotNull constraint, TID subTy, TID superTy) { - Unifier u{normalizer, Mode::Strict, constraint->scope, Location{}, Covariant}; + Unifier u{normalizer, Mode::Strict, constraint->scope, constraint->location, Covariant}; u.useScopes = true; u.tryUnify(subTy, superTy); @@ -2257,11 +2258,9 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l return errorRecoveryType(); } - std::string humanReadableName = moduleResolver->getHumanReadableModuleName(info.name); - for (const auto& [location, path] : requireCycles) { - if (!path.empty() && path.front() == humanReadableName) + if (!path.empty() && path.front() == (FFlag::LuauRequirePathTrueModuleName ? info.name : moduleResolver->getHumanReadableModuleName(info.name))) return builtinTypes->anyType; } @@ -2269,14 +2268,14 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l if (!module) { if (!moduleResolver->moduleExists(info.name) && !info.optional) - reportError(UnknownRequire{humanReadableName}, location); + reportError(UnknownRequire{moduleResolver->getHumanReadableModuleName(info.name)}, location); return errorRecoveryType(); } if (module->type != SourceCode::Type::Module) { - reportError(IllegalRequire{humanReadableName, "Module is not a ModuleScript. It cannot be required."}, location); + reportError(IllegalRequire{module->humanReadableName, "Module is not a ModuleScript. It cannot be required."}, location); return errorRecoveryType(); } @@ -2287,7 +2286,7 @@ TypeId ConstraintSolver::resolveModule(const ModuleInfo& info, const Location& l std::optional moduleType = first(modulePack); if (!moduleType) { - reportError(IllegalRequire{humanReadableName, "Module does not return exactly 1 value. It cannot be required."}, location); + reportError(IllegalRequire{module->humanReadableName, "Module does not return exactly 1 value. It cannot be required."}, location); return errorRecoveryType(); } diff --git a/Analysis/src/Error.cpp b/Analysis/src/Error.cpp index 84b9cb37..1e037972 100644 --- a/Analysis/src/Error.cpp +++ b/Analysis/src/Error.cpp @@ -11,6 +11,7 @@ #include LUAU_FASTFLAGVARIABLE(LuauTypeMismatchInvarianceInError, false) +LUAU_FASTFLAGVARIABLE(LuauRequirePathTrueModuleName, false) static std::string wrongNumberOfArgsString( size_t expectedCount, std::optional maximumCount, size_t actualCount, const char* argPrefix = nullptr, bool isVariadic = false) @@ -349,7 +350,10 @@ struct ErrorConverter else s += " -> "; - s += name; + if (FFlag::LuauRequirePathTrueModuleName && fileResolver != nullptr) + s += fileResolver->getHumanReadableModuleName(name); + else + s += name; } return s; diff --git a/Analysis/src/Frontend.cpp b/Analysis/src/Frontend.cpp index 5beb6c4e..916dd1d5 100644 --- a/Analysis/src/Frontend.cpp +++ b/Analysis/src/Frontend.cpp @@ -33,6 +33,7 @@ LUAU_FASTINTVARIABLE(LuauAutocompleteCheckTimeoutMs, 100) LUAU_FASTFLAGVARIABLE(DebugLuauDeferredConstraintResolution, false) LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false) LUAU_FASTFLAGVARIABLE(LuauOnDemandTypecheckers, false) +LUAU_FASTFLAG(LuauRequirePathTrueModuleName) namespace Luau { @@ -245,7 +246,7 @@ namespace { static ErrorVec accumulateErrors( - const std::unordered_map& sourceNodes, const std::unordered_map& modules, const ModuleName& name) + const std::unordered_map& sourceNodes, ModuleResolver& moduleResolver, const ModuleName& name) { std::unordered_set seen; std::vector queue{name}; @@ -271,11 +272,11 @@ static ErrorVec accumulateErrors( // FIXME: If a module has a syntax error, we won't be able to re-report it here. // The solution is probably to move errors from Module to SourceNode - auto it2 = modules.find(next); - if (it2 == modules.end()) + auto modulePtr = moduleResolver.getModule(next); + if (!modulePtr) continue; - Module& module = *it2->second; + Module& module = *modulePtr; std::sort(module.errors.begin(), module.errors.end(), [](const TypeError& e1, const TypeError& e2) -> bool { return e1.location.begin > e2.location.begin; @@ -345,9 +346,9 @@ std::vector getRequireCycles( if (top == start) { for (const SourceNode* node : path) - cycle.push_back(resolver->getHumanReadableModuleName(node->name)); + cycle.push_back(FFlag::LuauRequirePathTrueModuleName ? node->name : node->humanReadableName); - cycle.push_back(resolver->getHumanReadableModuleName(top->name)); + cycle.push_back(FFlag::LuauRequirePathTrueModuleName ? top->name : top->humanReadableName); break; } } @@ -415,11 +416,6 @@ Frontend::Frontend(FileResolver* fileResolver, ConfigResolver* configResolver, c { } -FrontendModuleResolver::FrontendModuleResolver(Frontend* frontend) - : frontend(frontend) -{ -} - CheckResult Frontend::check(const ModuleName& name, std::optional optionOverride) { LUAU_TIMETRACE_SCOPE("Frontend::check", "Frontend"); @@ -428,31 +424,21 @@ CheckResult Frontend::check(const ModuleName& name, std::optionalsecond.hasDirtyModule(frontendOptions.forAutocomplete)) { // No recheck required. - if (frontendOptions.forAutocomplete) - { - auto it2 = moduleResolverForAutocomplete.modules.find(name); - if (it2 == moduleResolverForAutocomplete.modules.end() || it2->second == nullptr) - throw InternalCompilerError("Frontend::modules does not have data for " + name, name); - } - else - { - auto it2 = moduleResolver.modules.find(name); - if (it2 == moduleResolver.modules.end() || it2->second == nullptr) - throw InternalCompilerError("Frontend::modules does not have data for " + name, name); - } + ModulePtr module = resolver.getModule(name); - std::unordered_map& modules = - frontendOptions.forAutocomplete ? moduleResolverForAutocomplete.modules : moduleResolver.modules; + if (!module) + throw InternalCompilerError("Frontend::modules does not have data for " + name, name); - checkResult.errors = accumulateErrors(sourceNodes, modules, name); + checkResult.errors = accumulateErrors(sourceNodes, resolver, name); // Get lint result only for top checked module - if (auto it = modules.find(name); it != modules.end()) - checkResult.lintResult = it->second->lintResult; + checkResult.lintResult = module->lintResult; return checkResult; } @@ -556,7 +542,7 @@ CheckResult Frontend::check(const ModuleName& name, std::optionalerrors.begin(), module->errors.end()); - moduleResolver.modules[moduleName] = std::move(module); + resolver.setModule(moduleName, std::move(module)); sourceNode.dirtyModule = false; } // Get lint result only for top checked module - std::unordered_map& modules = - frontendOptions.forAutocomplete ? moduleResolverForAutocomplete.modules : moduleResolver.modules; - - if (auto it = modules.find(name); it != modules.end()) - checkResult.lintResult = it->second->lintResult; + if (ModulePtr module = resolver.getModule(name)) + checkResult.lintResult = module->lintResult; return checkResult; } @@ -817,7 +800,7 @@ bool Frontend::isDirty(const ModuleName& name, bool forAutocomplete) const */ void Frontend::markDirty(const ModuleName& name, std::vector* markedDirty) { - if (!moduleResolver.modules.count(name) && !moduleResolverForAutocomplete.modules.count(name)) + if (!moduleResolver.getModule(name) && !moduleResolverForAutocomplete.getModule(name)) return; std::unordered_map> reverseDeps; @@ -884,13 +867,15 @@ ModulePtr check(const SourceModule& sourceModule, const std::vector(); + result->name = sourceModule.name; + result->humanReadableName = sourceModule.humanReadableName; result->reduction = std::make_unique(NotNull{&result->internalTypes}, builtinTypes, iceHandler); std::unique_ptr logger; if (recordJsonLog) { logger = std::make_unique(); - std::optional source = fileResolver->readSource(sourceModule.name); + std::optional source = fileResolver->readSource(result->name); if (source) { logger->captureSource(source->source); @@ -906,7 +891,6 @@ ModulePtr check(const SourceModule& sourceModule, const std::vectorinternalTypes, builtinTypes, NotNull{&unifierState}}; ConstraintGraphBuilder cgb{ - sourceModule.name, result, &result->internalTypes, moduleResolver, @@ -920,8 +904,8 @@ ModulePtr check(const SourceModule& sourceModule, const std::vectorerrors = std::move(cgb.errors); - ConstraintSolver cs{NotNull{&normalizer}, NotNull(cgb.rootScope), borrowConstraints(cgb.constraints), sourceModule.name, moduleResolver, - requireCycles, logger.get()}; + ConstraintSolver cs{ + NotNull{&normalizer}, NotNull(cgb.rootScope), borrowConstraints(cgb.constraints), result->name, moduleResolver, requireCycles, logger.get()}; if (options.randomizeConstraintResolutionSeed) cs.randomize(*options.randomizeConstraintResolutionSeed); @@ -936,7 +920,7 @@ ModulePtr check(const SourceModule& sourceModule, const std::vectorclonePublicInterface(builtinTypes, *iceHandler); - Luau::check(builtinTypes, logger.get(), sourceModule, result.get()); + Luau::check(builtinTypes, NotNull{&unifierState}, logger.get(), sourceModule, result.get()); // Ideally we freeze the arenas before the call into Luau::check, but TypeReduction // needs to allocate new types while Luau::check is in progress, so here we are. @@ -1033,7 +1017,8 @@ std::pair Frontend::getSourceNode(const ModuleName& sourceModule = std::move(result); sourceModule.environmentName = environmentName; - sourceNode.name = name; + sourceNode.name = sourceModule.name; + sourceNode.humanReadableName = sourceModule.humanReadableName; sourceNode.requireSet.clear(); sourceNode.requireLocations.clear(); sourceNode.dirtySourceModule = false; @@ -1095,6 +1080,7 @@ SourceModule Frontend::parse(const ModuleName& name, std::string_view src, const } sourceModule.name = name; + sourceModule.humanReadableName = fileResolver->getHumanReadableModuleName(name); if (parseOptions.captureComments) { @@ -1105,6 +1091,12 @@ SourceModule Frontend::parse(const ModuleName& name, std::string_view src, const return sourceModule; } + +FrontendModuleResolver::FrontendModuleResolver(Frontend* frontend) + : frontend(frontend) +{ +} + std::optional FrontendModuleResolver::resolveModuleInfo(const ModuleName& currentModuleName, const AstExpr& pathExpr) { // FIXME I think this can be pushed into the FileResolver. @@ -1129,6 +1121,8 @@ std::optional FrontendModuleResolver::resolveModuleInfo(const Module const ModulePtr FrontendModuleResolver::getModule(const ModuleName& moduleName) const { + std::scoped_lock lock(moduleMutex); + auto it = modules.find(moduleName); if (it != modules.end()) return it->second; @@ -1146,6 +1140,20 @@ std::string FrontendModuleResolver::getHumanReadableModuleName(const ModuleName& return frontend->fileResolver->getHumanReadableModuleName(moduleName); } +void FrontendModuleResolver::setModule(const ModuleName& moduleName, ModulePtr module) +{ + std::scoped_lock lock(moduleMutex); + + modules[moduleName] = std::move(module); +} + +void FrontendModuleResolver::clearModules() +{ + std::scoped_lock lock(moduleMutex); + + modules.clear(); +} + ScopePtr Frontend::addEnvironment(const std::string& environmentName) { LUAU_ASSERT(environments.count(environmentName) == 0); @@ -1208,8 +1216,8 @@ void Frontend::clear() { sourceNodes.clear(); sourceModules.clear(); - moduleResolver.modules.clear(); - moduleResolverForAutocomplete.modules.clear(); + moduleResolver.clearModules(); + moduleResolverForAutocomplete.clearModules(); requireTrace.clear(); } diff --git a/Analysis/src/Substitution.cpp b/Analysis/src/Substitution.cpp index 935d85d7..96217217 100644 --- a/Analysis/src/Substitution.cpp +++ b/Analysis/src/Substitution.cpp @@ -257,7 +257,7 @@ void Tarjan::visitChildren(TypeId ty, int index) } else if (const ClassType* ctv = get(ty); FFlag::LuauClassTypeVarsInSubstitution && ctv) { - for (auto [name, prop] : ctv->props) + for (const auto& [name, prop] : ctv->props) visitChild(prop.type); if (ctv->parent) diff --git a/Analysis/src/ToString.cpp b/Analysis/src/ToString.cpp index fe09ef11..46d2e8f8 100644 --- a/Analysis/src/ToString.cpp +++ b/Analysis/src/ToString.cpp @@ -834,8 +834,15 @@ struct TypeStringifier void operator()(TypeId, const LazyType& ltv) { - state.result.invalid = true; - state.emit("lazy?"); + if (TypeId unwrapped = ltv.unwrapped.load()) + { + stringify(unwrapped); + } + else + { + state.result.invalid = true; + state.emit("lazy?"); + } } void operator()(TypeId, const UnknownType& ttv) diff --git a/Analysis/src/Type.cpp b/Analysis/src/Type.cpp index 52854108..e4d9ab33 100644 --- a/Analysis/src/Type.cpp +++ b/Analysis/src/Type.cpp @@ -26,6 +26,7 @@ LUAU_FASTINTVARIABLE(LuauTableTypeMaximumStringifierLength, 0) LUAU_FASTINT(LuauTypeInferRecursionLimit) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauNormalizeBlockedTypes) +LUAU_FASTFLAGVARIABLE(LuauBoundLazyTypes, false) namespace Luau { @@ -56,18 +57,51 @@ TypeId follow(TypeId t) TypeId follow(TypeId t, std::function mapper) { auto advance = [&mapper](TypeId ty) -> std::optional { - if (auto btv = get>(mapper(ty))) - return btv->boundTo; - else if (auto ttv = get(mapper(ty))) - return ttv->boundTo; - else + if (FFlag::LuauBoundLazyTypes) + { + TypeId mapped = mapper(ty); + + if (auto btv = get>(mapped)) + return btv->boundTo; + + if (auto ttv = get(mapped)) + return ttv->boundTo; + + if (auto ltv = getMutable(mapped)) + { + TypeId unwrapped = ltv->unwrapped.load(); + + if (unwrapped) + return unwrapped; + + unwrapped = ltv->unwrap(*ltv); + + if (!unwrapped) + throw InternalCompilerError("Lazy Type didn't fill in unwrapped type field"); + + if (get(unwrapped)) + throw InternalCompilerError("Lazy Type cannot resolve to another Lazy Type"); + + return unwrapped; + } + return std::nullopt; + } + else + { + if (auto btv = get>(mapper(ty))) + return btv->boundTo; + else if (auto ttv = get(mapper(ty))) + return ttv->boundTo; + else + return std::nullopt; + } }; auto force = [&mapper](TypeId ty) { if (auto ltv = get_if(&mapper(ty)->ty)) { - TypeId res = ltv->thunk(); + TypeId res = ltv->thunk_DEPRECATED(); if (get(res)) throw InternalCompilerError("Lazy Type cannot resolve to another Lazy Type"); @@ -75,7 +109,8 @@ TypeId follow(TypeId t, std::function mapper) } }; - force(t); + if (!FFlag::LuauBoundLazyTypes) + force(t); TypeId cycleTester = t; // Null once we've determined that there is no cycle if (auto a = advance(cycleTester)) @@ -85,7 +120,9 @@ TypeId follow(TypeId t, std::function mapper) while (true) { - force(t); + if (!FFlag::LuauBoundLazyTypes) + force(t); + auto a1 = advance(t); if (a1) t = *a1; diff --git a/Analysis/src/TypeAttach.cpp b/Analysis/src/TypeAttach.cpp index 715a8322..7ed4eb49 100644 --- a/Analysis/src/TypeAttach.cpp +++ b/Analysis/src/TypeAttach.cpp @@ -344,6 +344,9 @@ public: } AstType* operator()(const LazyType& ltv) { + if (TypeId unwrapped = ltv.unwrapped.load()) + return Luau::visit(*this, unwrapped->ty); + return allocator->alloc(Location(), std::nullopt, AstName(""), std::nullopt, Location()); } AstType* operator()(const UnknownType& ttv) diff --git a/Analysis/src/TypeChecker2.cpp b/Analysis/src/TypeChecker2.cpp index 6e76af04..893f51d9 100644 --- a/Analysis/src/TypeChecker2.cpp +++ b/Analysis/src/TypeChecker2.cpp @@ -88,21 +88,22 @@ struct TypeChecker2 { NotNull builtinTypes; DcrLogger* logger; - InternalErrorReporter ice; // FIXME accept a pointer from Frontend + NotNull ice; const SourceModule* sourceModule; Module* module; TypeArena testArena; std::vector> stack; - UnifierSharedState sharedState{&ice}; - Normalizer normalizer{&testArena, builtinTypes, NotNull{&sharedState}}; + Normalizer normalizer; - TypeChecker2(NotNull builtinTypes, DcrLogger* logger, const SourceModule* sourceModule, Module* module) + TypeChecker2(NotNull builtinTypes, NotNull unifierState, DcrLogger* logger, const SourceModule* sourceModule, Module* module) : builtinTypes(builtinTypes) , logger(logger) + , ice(unifierState->iceHandler) , sourceModule(sourceModule) , module(module) + , normalizer{&testArena, builtinTypes, unifierState} { } @@ -996,7 +997,7 @@ struct TypeChecker2 } if (!fst) - ice.ice("UnionType had no elements, so fst is nullopt?"); + ice->ice("UnionType had no elements, so fst is nullopt?"); if (std::optional instantiatedFunctionType = instantiation.substitute(*fst)) { @@ -1018,7 +1019,7 @@ struct TypeChecker2 { AstExprIndexName* indexExpr = call->func->as(); if (!indexExpr) - ice.ice("method call expression has no 'self'"); + ice->ice("method call expression has no 'self'"); args.head.push_back(lookupType(indexExpr->expr)); argLocs.push_back(indexExpr->expr->location); @@ -1646,7 +1647,7 @@ struct TypeChecker2 else if (finite(pack) && size(pack) == 0) return builtinTypes->nilType; // `(f())` where `f()` returns no values is coerced into `nil` else - ice.ice("flattenPack got a weird pack!"); + ice->ice("flattenPack got a weird pack!"); } void visitGenerics(AstArray generics, AstArray genericPacks) @@ -2012,7 +2013,7 @@ struct TypeChecker2 void reportError(TypeErrorData data, const Location& location) { - module->errors.emplace_back(location, sourceModule->name, std::move(data)); + module->errors.emplace_back(location, module->name, std::move(data)); if (logger) logger->captureTypeCheckError(module->errors.back()); @@ -2160,9 +2161,9 @@ struct TypeChecker2 } }; -void check(NotNull builtinTypes, DcrLogger* logger, const SourceModule& sourceModule, Module* module) +void check(NotNull builtinTypes, NotNull unifierState, DcrLogger* logger, const SourceModule& sourceModule, Module* module) { - TypeChecker2 typeChecker{builtinTypes, logger, &sourceModule, module}; + TypeChecker2 typeChecker{builtinTypes, unifierState, logger, &sourceModule, module}; typeChecker.reduceTypes(); typeChecker.visit(sourceModule.root); diff --git a/Analysis/src/TypeInfer.cpp b/Analysis/src/TypeInfer.cpp index 7f366a20..a8c093a4 100644 --- a/Analysis/src/TypeInfer.cpp +++ b/Analysis/src/TypeInfer.cpp @@ -33,7 +33,6 @@ LUAU_FASTINTVARIABLE(LuauCheckRecursionLimit, 300) LUAU_FASTINTVARIABLE(LuauVisitRecursionLimit, 500) LUAU_FASTFLAG(LuauKnowsTheDataModel3) LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false) -LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false. LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false) LUAU_FASTFLAG(LuauInstantiateInSubtyping) LUAU_FASTFLAG(LuauNegatedClassTypes) @@ -42,6 +41,7 @@ LUAU_FASTFLAG(LuauUninhabitedSubAnything2) LUAU_FASTFLAG(LuauOccursIsntAlwaysFailure) LUAU_FASTFLAGVARIABLE(LuauTypecheckTypeguards, false) LUAU_FASTFLAGVARIABLE(LuauTinyControlFlowAnalysis, false) +LUAU_FASTFLAG(LuauRequirePathTrueModuleName) namespace Luau { @@ -264,8 +264,11 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo { LUAU_TIMETRACE_SCOPE("TypeChecker::check", "TypeChecker"); LUAU_TIMETRACE_ARGUMENT("module", module.name.c_str()); + LUAU_TIMETRACE_ARGUMENT("name", module.humanReadableName.c_str()); currentModule.reset(new Module); + currentModule->name = module.name; + currentModule->humanReadableName = module.humanReadableName; currentModule->reduction = std::make_unique(NotNull{¤tModule->internalTypes}, builtinTypes, NotNull{iceHandler}); currentModule->type = module.type; currentModule->allocator = module.allocator; @@ -290,10 +293,8 @@ ModulePtr TypeChecker::checkWithoutRecursionCheck(const SourceModule& module, Mo currentModule->scopes.push_back(std::make_pair(module.root->location, moduleScope)); currentModule->mode = mode; - currentModuleName = module.name; - if (prepareModuleScope) - prepareModuleScope(module.name, currentModule->getModuleScope()); + prepareModuleScope(currentModule->name, currentModule->getModuleScope()); try { @@ -1179,7 +1180,7 @@ ControlFlow TypeChecker::check(const ScopePtr& scope, const AstStatLocal& local) { AstExpr* require = *maybeRequire; - if (auto moduleInfo = resolver->resolveModuleInfo(currentModuleName, *require)) + if (auto moduleInfo = resolver->resolveModuleInfo(currentModule->name, *require)) { const Name name{local.vars.data[i]->name.value}; @@ -1728,7 +1729,7 @@ void TypeChecker::prototype(const ScopePtr& scope, const AstStatDeclareClass& de Name className(declaredClass.name.value); - TypeId classTy = addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, currentModuleName)); + TypeId classTy = addType(ClassType(className, {}, superTy, std::nullopt, {}, {}, currentModule->name)); ClassType* ctv = getMutable(classTy); TypeId metaTy = addType(TableType{TableState::Sealed, scope->level}); @@ -2000,12 +2001,7 @@ WithPredicate TypeChecker::checkExpr(const ScopePtr& scope, const AstExp else if (auto vtp = get(retPack)) return {vtp->ty, std::move(result.predicates)}; else if (get(retPack)) - { - if (FFlag::LuauReturnAnyInsteadOfICE) - return {anyType, std::move(result.predicates)}; - else - ice("Unexpected abstract type pack!", expr.location); - } + return {anyType, std::move(result.predicates)}; else ice("Unknown TypePack type!", expr.location); } @@ -2336,7 +2332,7 @@ TypeId TypeChecker::checkExprTable( TableState state = TableState::Unsealed; TableType table = TableType{std::move(props), indexer, scope->level, state}; - table.definitionModuleName = currentModuleName; + table.definitionModuleName = currentModule->name; table.definitionLocation = expr.location; return addType(table); } @@ -3663,7 +3659,7 @@ std::pair TypeChecker::checkFunctionSignature(const ScopePtr& TypePackId argPack = addTypePack(TypePackVar(TypePack{argTypes, funScope->varargPack})); FunctionDefinition defn; - defn.definitionModuleName = currentModuleName; + defn.definitionModuleName = currentModule->name; defn.definitionLocation = expr.location; defn.varargLocation = expr.vararg ? std::make_optional(expr.varargLocation) : std::nullopt; defn.originalNameLocation = originalName.value_or(Location(expr.location.begin, 0)); @@ -4606,11 +4602,9 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module } // Types of requires that transitively refer to current module have to be replaced with 'any' - std::string humanReadableName = resolver->getHumanReadableModuleName(moduleInfo.name); - for (const auto& [location, path] : requireCycles) { - if (!path.empty() && path.front() == humanReadableName) + if (!path.empty() && path.front() == (FFlag::LuauRequirePathTrueModuleName ? moduleInfo.name : resolver->getHumanReadableModuleName(moduleInfo.name))) return anyType; } @@ -4621,14 +4615,14 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module // either the file does not exist or there's a cycle. If there's a cycle // we will already have reported the error. if (!resolver->moduleExists(moduleInfo.name) && !moduleInfo.optional) - reportError(TypeError{location, UnknownRequire{humanReadableName}}); + reportError(TypeError{location, UnknownRequire{resolver->getHumanReadableModuleName(moduleInfo.name)}}); return errorRecoveryType(scope); } if (module->type != SourceCode::Module) { - reportError(location, IllegalRequire{humanReadableName, "Module is not a ModuleScript. It cannot be required."}); + reportError(location, IllegalRequire{module->humanReadableName, "Module is not a ModuleScript. It cannot be required."}); return errorRecoveryType(scope); } @@ -4640,7 +4634,7 @@ TypeId TypeChecker::checkRequire(const ScopePtr& scope, const ModuleInfo& module std::optional moduleType = first(modulePack); if (!moduleType) { - reportError(location, IllegalRequire{humanReadableName, "Module does not return exactly 1 value. It cannot be required."}); + reportError(location, IllegalRequire{module->humanReadableName, "Module does not return exactly 1 value. It cannot be required."}); return errorRecoveryType(scope); } @@ -4855,7 +4849,7 @@ void TypeChecker::reportError(const TypeError& error) if (currentModule->mode == Mode::NoCheck) return; currentModule->errors.push_back(error); - currentModule->errors.back().moduleName = currentModuleName; + currentModule->errors.back().moduleName = currentModule->name; } void TypeChecker::reportError(const Location& location, TypeErrorData errorData) @@ -5329,7 +5323,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno tableIndexer = TableIndexer(resolveType(scope, *indexer->indexType), resolveType(scope, *indexer->resultType)); TableType ttv{props, tableIndexer, scope->level, TableState::Sealed}; - ttv.definitionModuleName = currentModuleName; + ttv.definitionModuleName = currentModule->name; ttv.definitionLocation = annotation.location; return addType(std::move(ttv)); } @@ -5531,7 +5525,7 @@ TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, { ttv->instantiatedTypeParams = typeParams; ttv->instantiatedTypePackParams = typePackParams; - ttv->definitionModuleName = currentModuleName; + ttv->definitionModuleName = currentModule->name; ttv->definitionLocation = location; } diff --git a/Analysis/src/TypeReduction.cpp b/Analysis/src/TypeReduction.cpp index 310df766..031c5993 100644 --- a/Analysis/src/TypeReduction.cpp +++ b/Analysis/src/TypeReduction.cpp @@ -10,7 +10,7 @@ #include LUAU_FASTINTVARIABLE(LuauTypeReductionCartesianProductLimit, 100'000) -LUAU_FASTINTVARIABLE(LuauTypeReductionRecursionLimit, 400) +LUAU_FASTINTVARIABLE(LuauTypeReductionRecursionLimit, 300) LUAU_FASTFLAGVARIABLE(DebugLuauDontReduceTypes, false) namespace Luau diff --git a/CLI/Repl.cpp b/CLI/Repl.cpp index 63baea8b..bcf70f25 100644 --- a/CLI/Repl.cpp +++ b/CLI/Repl.cpp @@ -8,6 +8,7 @@ #include "Luau/Compiler.h" #include "Luau/BytecodeBuilder.h" #include "Luau/Parser.h" +#include "Luau/TimeTrace.h" #include "Coverage.h" #include "FileUtils.h" @@ -997,15 +998,18 @@ int replMain(int argc, char** argv) CompileStats stats = {}; int failed = 0; + double startTime = Luau::TimeTrace::getClock(); for (const std::string& path : files) failed += !compileFile(path.c_str(), compileFormat, stats); + double duration = Luau::TimeTrace::getClock() - startTime; + if (compileFormat == CompileFormat::Null) - printf("Compiled %d KLOC into %d KB bytecode\n", int(stats.lines / 1000), int(stats.bytecode / 1024)); + printf("Compiled %d KLOC into %d KB bytecode in %.2fs\n", int(stats.lines / 1000), int(stats.bytecode / 1024), duration); else if (compileFormat == CompileFormat::CodegenNull) - printf("Compiled %d KLOC into %d KB bytecode => %d KB native code\n", int(stats.lines / 1000), int(stats.bytecode / 1024), - int(stats.codegen / 1024)); + printf("Compiled %d KLOC into %d KB bytecode => %d KB native code (%.2fx) in %.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), duration); return failed ? 1 : 0; } diff --git a/CodeGen/include/Luau/AssemblyBuilderA64.h b/CodeGen/include/Luau/AssemblyBuilderA64.h index 42f5f8a6..1a5f5137 100644 --- a/CodeGen/include/Luau/AssemblyBuilderA64.h +++ b/CodeGen/include/Luau/AssemblyBuilderA64.h @@ -37,7 +37,6 @@ public: void movk(RegisterA64 dst, uint16_t src, int shift = 0); // Arithmetics - // TODO: support various kinds of shifts void add(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift = 0); void add(RegisterA64 dst, RegisterA64 src1, uint16_t src2); void sub(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift = 0); @@ -52,13 +51,11 @@ public: void cset(RegisterA64 dst, ConditionA64 cond); // Bitwise - // TODO: support shifts - // TODO: support bitfield ops - void and_(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2); - void orr(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2); - void eor(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2); - void bic(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2); - void tst(RegisterA64 src1, RegisterA64 src2); + void and_(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift = 0); + void orr(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift = 0); + void eor(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift = 0); + void bic(RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, int shift = 0); + void tst(RegisterA64 src1, RegisterA64 src2, int shift = 0); void mvn(RegisterA64 dst, RegisterA64 src); // Bitwise with immediate @@ -76,6 +73,13 @@ public: void clz(RegisterA64 dst, RegisterA64 src); void rbit(RegisterA64 dst, RegisterA64 src); + // Shifts with immediates + // Note: immediate value must be in [0, 31] or [0, 63] range based on register type + void lsl(RegisterA64 dst, RegisterA64 src1, uint8_t src2); + void lsr(RegisterA64 dst, RegisterA64 src1, uint8_t src2); + void asr(RegisterA64 dst, RegisterA64 src1, uint8_t src2); + void ror(RegisterA64 dst, RegisterA64 src1, uint8_t src2); + // Load // Note: paired loads are currently omitted for simplicity void ldr(RegisterA64 dst, AddressA64 src); @@ -93,15 +97,19 @@ public: void stp(RegisterA64 src1, RegisterA64 src2, AddressA64 dst); // Control flow - // TODO: support tbz/tbnz; they have 15-bit offsets but they can be useful in constrained cases void b(Label& label); - void b(ConditionA64 cond, Label& label); - void cbz(RegisterA64 src, Label& label); - void cbnz(RegisterA64 src, Label& label); + void bl(Label& label); void br(RegisterA64 src); void blr(RegisterA64 src); void ret(); + // Conditional control flow + void b(ConditionA64 cond, Label& label); + void cbz(RegisterA64 src, Label& label); + void cbnz(RegisterA64 src, Label& label); + void tbz(RegisterA64 src, uint8_t bit, Label& label); + void tbnz(RegisterA64 src, uint8_t bit, Label& label); + // Address of embedded data void adr(RegisterA64 dst, const void* ptr, size_t size); void adr(RegisterA64 dst, uint64_t value); @@ -111,7 +119,9 @@ public: void adr(RegisterA64 dst, Label& label); // Floating-point scalar moves + // Note: constant must be compatible with immediate floating point moves (see isFmovSupported) void fmov(RegisterA64 dst, RegisterA64 src); + void fmov(RegisterA64 dst, double src); // Floating-point scalar math void fabs(RegisterA64 dst, RegisterA64 src); @@ -173,6 +183,12 @@ public: // Maximum immediate argument to functions like add/sub/cmp static constexpr size_t kMaxImmediate = (1 << 12) - 1; + // Check if immediate mode mask is supported for bitwise operations (and/or/xor) + static bool isMaskSupported(uint32_t mask); + + // Check if fmov can be used to synthesize a constant + static bool isFmovSupported(double value); + private: // Instruction archetypes void place0(const char* name, uint32_t word); @@ -183,20 +199,38 @@ private: void placeI12(const char* name, RegisterA64 dst, RegisterA64 src1, int src2, uint8_t op); void placeI16(const char* name, RegisterA64 dst, int src, uint8_t op, int shift = 0); void placeA(const char* name, RegisterA64 dst, AddressA64 src, uint8_t op, uint8_t size, int sizelog); + void placeB(const char* name, Label& label, uint8_t op); void placeBC(const char* name, Label& label, uint8_t op, uint8_t cond); void placeBCR(const char* name, Label& label, uint8_t op, RegisterA64 cond); void placeBR(const char* name, RegisterA64 src, uint32_t op); + void placeBTR(const char* name, Label& label, uint8_t op, RegisterA64 cond, uint8_t bit); void placeADR(const char* name, RegisterA64 src, uint8_t op); void placeADR(const char* name, RegisterA64 src, uint8_t op, Label& label); void placeP(const char* name, RegisterA64 dst1, RegisterA64 dst2, AddressA64 src, uint8_t op, uint8_t opc, int sizelog); void placeCS(const char* name, RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, ConditionA64 cond, uint8_t op, uint8_t opc, int invert = 0); void placeFCMP(const char* name, RegisterA64 src1, RegisterA64 src2, uint8_t op, uint8_t opc); + void placeFMOV(const char* name, RegisterA64 dst, double src, uint32_t op); void placeBM(const char* name, RegisterA64 dst, RegisterA64 src1, uint32_t src2, uint8_t op); + void placeBFM(const char* name, RegisterA64 dst, RegisterA64 src1, uint8_t src2, uint8_t op, int immr, int imms); void place(uint32_t word); - void patchLabel(Label& label); - void patchImm19(uint32_t location, int value); + struct Patch + { + enum Kind + { + Imm26, + Imm19, + Imm14, + }; + + Kind kind : 2; + uint32_t label : 30; + uint32_t location; + }; + + void patchLabel(Label& label, Patch::Kind kind); + void patchOffset(uint32_t location, int value, Patch::Kind kind); void commit(); LUAU_NOINLINE void extend(); @@ -210,9 +244,10 @@ private: LUAU_NOINLINE void log(const char* opcode, RegisterA64 dst, RegisterA64 src1, int src2); LUAU_NOINLINE void log(const char* opcode, RegisterA64 dst, RegisterA64 src); LUAU_NOINLINE void log(const char* opcode, RegisterA64 dst, int src, int shift = 0); + LUAU_NOINLINE void log(const char* opcode, RegisterA64 dst, double src); LUAU_NOINLINE void log(const char* opcode, RegisterA64 dst, AddressA64 src); LUAU_NOINLINE void log(const char* opcode, RegisterA64 dst1, RegisterA64 dst2, AddressA64 src); - LUAU_NOINLINE void log(const char* opcode, RegisterA64 src, Label label); + LUAU_NOINLINE void log(const char* opcode, RegisterA64 src, Label label, int imm = -1); LUAU_NOINLINE void log(const char* opcode, RegisterA64 src); LUAU_NOINLINE void log(const char* opcode, Label label); LUAU_NOINLINE void log(const char* opcode, RegisterA64 dst, RegisterA64 src1, RegisterA64 src2, ConditionA64 cond); @@ -221,7 +256,7 @@ private: LUAU_NOINLINE void log(AddressA64 addr); uint32_t nextLabel = 1; - std::vector