Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ class MLIRGenImpl

int processStatements(NodeArray<Statement> statements,
const GenContext &genContext,
bool isRoot = false);
bool isRoot = false,
bool skipCode = false);

bool hasGlobalCode(NodeArray<Statement> statements);

Expand All @@ -251,6 +252,10 @@ class MLIRGenImpl
mlir::LogicalResult generateGlobalEntryCode(mlir::Location location, NodeArray<Statement> statements,
bool hasDeferredStatements, const GenContext &genContext);

// an imported module's top-level statements, run from a global constructor
mlir::LogicalResult generateModuleInitCode(mlir::Location location, NodeArray<Statement> statements,
const GenContext &genContext);

mlir::LogicalResult outputDiagnostics(mlir::SmallVector<std::unique_ptr<mlir::Diagnostic>> &postponedMessages,
int notResolved);

Expand Down
63 changes: 60 additions & 3 deletions tslang/lib/TypeScript/MLIRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,8 @@ namespace mlirgen

int MLIRGenImpl::processStatements(NodeArray<Statement> statements,
const GenContext &genContext,
bool isRoot)
bool isRoot,
bool skipCode)
{
clearState(statements);

Expand All @@ -654,6 +655,11 @@ namespace mlirgen
continue;
}

if (skipCode && isCodeStatment(statement))
{
continue;
}

if (failed(mlirGen(statement, genContext)))
{
emitError(loc(statement), "failed statement");
Expand Down Expand Up @@ -749,7 +755,16 @@ namespace mlirgen
std::string name = MAIN_ENTRY_NAME;
auto fullGlobalFuncName = getFullNamespaceName(name);

if (theModule.lookupSymbol(fullGlobalFuncName))
// A library - a module linked beside the program, or one the JIT cache compiles for an
// import - runs its top level from a global constructor: a `main` of its own was a second
// one at link time (#447).
if (!compileOptions.generateEntryPoint && hasDeferredStatements && !theModule.lookupSymbol(fullGlobalFuncName))
{
name = MLIRHelper::getAnonymousName(location, "." MAIN_ENTRY_NAME, "");
fullGlobalFuncName = getFullNamespaceName(name);
useGlobalCtor = true;
}
else if (theModule.lookupSymbol(fullGlobalFuncName))
{
// a user-written `main` already is the entry point, so with nothing deferred to run
// ahead of it there is nothing left to generate
Expand Down Expand Up @@ -820,6 +835,39 @@ namespace mlirgen
return mlir::success();
}

// An imported module's top-level statements - `print("module init")` beside its exports - run
// when the program starts, from a global constructor of their own, after the module's variables
// are initialized. They were generated at the importer's module level, outside any function,
// which LLVM rejected ("Global is referenced by parentless instruction", #447).
mlir::LogicalResult MLIRGenImpl::generateModuleInitCode(mlir::Location location, NodeArray<Statement> statements,
const GenContext &genContext)
{
mlir::OpBuilder::InsertionGuard insertGuard(builder);

auto name = MLIRHelper::getAnonymousName(location, ".init", "");
auto fullName = getFullNamespaceName(name);
auto funcType = getFunctionType({}, {}, false);
if (mlir::failed(mlirGenFunctionBody(location, name, fullName, funcType,
[&](mlir::Location location, const GenContext &genContext) {
for (auto &statement : statements)
{
if (isCodeStatment(statement) && failed(mlirGen(statement, genContext)))
{
emitError(loc(statement), "failed statement");
return mlir::failure();
}
}

return mlir::success();
}, genContext, 0, true)))
{
return mlir::failure();
}

addGlobalConstructor(location, fullName);
return mlir::success();
}

mlir::LogicalResult MLIRGenImpl::outputDiagnostics(mlir::SmallVector<std::unique_ptr<mlir::Diagnostic>> &postponedMessages,
int notResolved)
{
Expand Down Expand Up @@ -950,7 +998,16 @@ namespace mlirgen
}

auto anyGlobalCode = hasGlobalCode(module->statements);
auto notResolved = processStatements(module->statements, genContext, isMain && anyGlobalCode);
// An imported module's top-level code is never generated at this module's level: read as
// declarations it is its own object's to run, and included with its bodies it runs from a
// global constructor (generateModuleInitCode).
auto notResolved = processStatements(module->statements, genContext, isMain && anyGlobalCode, !isMain);
if (notResolved == 0 && !isMain && anyGlobalCode && !declarationMode &&
mlir::failed(generateModuleInitCode(loc(module), module->statements, genContext)))
{
notResolved = 1;
}

if (failed(outputDiagnostics(postponedMessages, notResolved)))
{
return mlir::failure();
Expand Down
5 changes: 5 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3043,6 +3043,11 @@ set_tests_properties(test-compile-reference-path-import-cycle-quiet PROPERTIES F
set(import_source_dir "${PROJECT_SOURCE_DIR}/test/tester/import-source")
tslang_add_test(NAME test-jit-import-source-new-imported-class COMMAND test-runner -jit "${import_source_dir}/new_imported_class.ts")
tslang_add_import_tests(import-source-new-imported-class "${import_source_dir}" new_imported_class imported_class_module)
# A module with top-level statements, imported: they run before main, from a global constructor;
# generated outside any function, they failed with "Global is referenced by parentless
# instruction", and a module linked beside the program got a `main` of its own (#447).
tslang_add_test(NAME test-jit-import-source-toplevel COMMAND test-runner -jit "${import_source_dir}/import_toplevel.ts")
tslang_add_import_tests(import-source-toplevel "${import_source_dir}" import_toplevel toplevel_module)

# The JIT cache (--emit=jit, on by default): each .ts module imported is an object of its own,
# loaded beside the program's - imported through two others and initialized first; and a program
Expand Down
9 changes: 9 additions & 0 deletions tslang/test/tester/import-source/import_toplevel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// The importer of a module with top-level statements (#447): the module's statements have run,
// once, by the time main does.
import "./toplevel_module";

function main() {
assert(startCount() == 1, "the module's top level ran once, before main");
assert(twice(21) == 42, "its function");
print("done.");
}
15 changes: 15 additions & 0 deletions tslang/test/tester/import-source/toplevel_module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// A module with top-level statements, imported (#447): they run when the program starts, before
// the importer's main. Generated at the importer's module level, outside any function, they were
// invalid LLVM IR ("Global is referenced by parentless instruction").
let started = 0;

export function twice(n: number) {
return n * 2;
}

export function startCount() {
return started;
}

started = started + 1;
print("module init");
Loading