Skip to content

[Macros] Add executable plugin support #63793

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 23, 2023

Conversation

rintaro
Copy link
Member

@rintaro rintaro commented Feb 21, 2023

Executable compiler plugins are programs invoked by the host compiler and communicate with the host with IPC via standard IO (stdin/stdout.) Each message is serialized in JSON, prefixed with a header which is a 64bit little-endian integer indicating the size of the message.

  • Basic/ExecuteWithPipe: External program invocation. Like llvm::sys::ExecuteNoWait() but establishing pipes to the child's stdin/stdout
  • Basic/Sandbox: Sandboxed execution helper. Mutates command line arguments to be executed in sandbox environment (similar to SwiftPM's plugin sandbox)
  • AST/PluginRepository: ASTContext independent plugin manager.
  • ASTGen/PluginHost: Communication with the plugin. Messages are serialized by ASTGen/LLVMJSON added in [ASTGen] Add Encoder/Decoder implementation backed by llvm::json::Value #63689

This PR implements 3 messages (ASTGen/PluginMessages).

  • getCapability: get plugin capability. currently it only returns fixed protocolVersion 1.
  • expandFreestandingMacro: expand freestanding macros
  • expandAttachedMacro: expand attached macros

rdar://101508815

@rintaro
Copy link
Member Author

rintaro commented Feb 21, 2023

@swift-ci Please smoke test

@rintaro rintaro force-pushed the macro-plugin-executable branch 2 times, most recently from e5e8e3e to c90931e Compare February 21, 2023 03:51
@rintaro
Copy link
Member Author

rintaro commented Feb 21, 2023

@swift-ci Please smoke test

@rintaro rintaro force-pushed the macro-plugin-executable branch from c90931e to 97bfd88 Compare February 21, 2023 05:10
@rintaro
Copy link
Member Author

rintaro commented Feb 21, 2023

@swift-ci Please smoke test

@rintaro rintaro force-pushed the macro-plugin-executable branch from 97bfd88 to b940a47 Compare February 21, 2023 06:25
@rintaro
Copy link
Member Author

rintaro commented Feb 21, 2023

@swift-ci Please smoke test

@rintaro rintaro force-pushed the macro-plugin-executable branch 3 times, most recently from 9040212 to 6fefff5 Compare February 21, 2023 23:53
@rintaro
Copy link
Member Author

rintaro commented Feb 21, 2023

@swift-ci Please smoke test

@rintaro rintaro requested a review from bnbarham February 22, 2023 00:58
@rintaro rintaro force-pushed the macro-plugin-executable branch from 6fefff5 to 43f3364 Compare February 22, 2023 17:03
@rintaro
Copy link
Member Author

rintaro commented Feb 22, 2023

@swift-ci Please Python lint

@rintaro rintaro force-pushed the macro-plugin-executable branch from 43f3364 to dd256f2 Compare February 22, 2023 18:04
@rintaro
Copy link
Member Author

rintaro commented Feb 22, 2023

@swift-ci Please test

@rintaro rintaro changed the title [WIP][Macros] Add executable plugin support [Macros] Add executable plugin support Feb 22, 2023
@rintaro rintaro marked this pull request as ready for review February 22, 2023 18:05
Executable compiler plugins are programs invoked by the host compiler
and communicate with the host with IPC via standard IO (stdin/stdout.)
Each message is serialized in JSON, prefixed with a header which is a
64bit little-endian integer indicating the size of the message.

* Basic/ExecuteWithPipe: External program invocation. Lik
  llvm::sys::ExecuteNoWait() but establishing pipes to the child's
  stdin/stdout
* Basic/Sandbox: Sandboxed execution helper. Create command line
  arguments to be executed in sandbox environment (similar to SwiftPM's
  pluging sandbox)
* AST/PluginRepository: ASTContext independent plugin manager
* ASTGen/PluginHost: Communication with the plugin. Messages are
  serialized by ASTGen/LLVMJSON

rdar://101508815
@rintaro rintaro force-pushed the macro-plugin-executable branch from dd256f2 to 0e31393 Compare February 22, 2023 18:22
@rintaro
Copy link
Member Author

rintaro commented Feb 22, 2023

@swift-ci Please test

@xedin xedin removed their request for review February 22, 2023 18:23
Copy link
Contributor

@bnbarham bnbarham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very cool!

/// pointer, it's not used in AST at all.
void Plugin_setCapability(void *handle, const void *data);

/// Get a coapability data set by \c Plugin_setCapability .
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Get a coapability data set by \c Plugin_setCapability .
/// Get a capability data set by \c Plugin_setCapability .


/// Set a capability data to the plugin object. Since the data is just a opaque
/// pointer, it's not used in AST at all.
void Plugin_setCapability(void *handle, const void *data);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on adding typedefs for the various void*? I know they're all still void* regardless, it just makes it easier when reading through the API (IMO).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. done.


// Execute the program.
if (env.has_value()) {
const char **envp = toNullTerminatedCStringArray(*env, Alloc);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have envp above, assuming I'm reading the #if's correctly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I added "has posix_spawn" branch after fork implementation. This was a leftover.

Comment on lines 61 to 62
/// \param env An optional array of strings to use for the program's
/// environment.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe worth saying it's an array of "key=value" strings?


/// Cache of loaded symbols.
llvm::StringMap<void *> LoadedSymbols;

/// Map a module name to an executable plugin path that provides the module.
llvm::DenseMap<Identifier, std::string> ExecutablePluginPaths;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could shove the paths into the ASTContext's allocator instead since we never remove them. Doesn't really matter, but would mean our size calculations are a little more accurate. (see AllocateCopy)

}

static Optional<ExternalMacroDefinition>
resolvExecutableMacro(ASTContext &ctx, Identifier moduleName,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
resolvExecutableMacro(ASTContext &ctx, Identifier moduleName,
resolveExecutableMacro(ASTContext &ctx, Identifier moduleName,

});
}

if (auto execMacro = swift_ASTGen_resolveExecutableMacro(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice to keep the * with auto to make it obvious IMO, up to you though.

# respond. For example:
# {'expected': {'testRequest': {'params': []}},
# 'response': {'testRequestResult': [1,2,"foo"]}}
# this spec matches a request that is an object with 'testRequst' key with an
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# this spec matches a request that is an object with 'testRequst' key with an
# this spec matches a request that is an object with 'testRequest' key with an

moduleNameLength: Int,
typeName: UnsafePointer<UInt8>,
typeNameLength: Int,
pluginOpaqueHanle: UnsafeMutableRawPointer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pluginOpaqueHanle: UnsafeMutableRawPointer
pluginOpaqueHandle: UnsafeMutableRawPointer

let discriminator = String(decoding: discriminatorBuffer, as: UTF8.self)

let expandedSources: [String]?
switch MacroPluginKind(rawValue: macroKind)! {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly over engineering here, but we could make a protocol with the two expand functions instead. They could be static or actual methods, where MacroPluginKind has an expander that returns the type or a new struct (depending on whether you choose static methods or not):

enum MacroPluginKind {
...
  var expander: some MacroExpander {
    switch ...
  }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea. Let me do it in a follow-ups because it changes the indentation of the current code, which makes the change set large.

@rintaro
Copy link
Member Author

rintaro commented Feb 23, 2023

@swift-ci Please test

@rintaro
Copy link
Member Author

rintaro commented Feb 23, 2023

@swift-ci Please test

}
#else
lib = dlopen(path.str().c_str(), RTLD_LAZY | RTLD_LOCAL);
if (!lib) {
return llvm::createStringError(std::errc::not_supported,
"unsupported platform");
return llvm::createStringError(std::error_code(), dlerror());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's no suitable errc here it's probably better to use inconvertibleErrorCode

@@ -651,14 +651,19 @@ void Plugin_unlock(PluginHandle handle) {
bool Plugin_sendMessage(PluginHandle handle, const BridgedData data) {
auto *plugin = static_cast<LoadedExecutablePlugin *>(handle);
StringRef message(data.baseAddress, data.size);
return plugin->sendMessage(message);
return bool(plugin->sendMessage(message));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fairly sure this will still fail when there's an error. LLVM has a consumeError to just eat it, though that makes me wonder if we should be passing in an error string to set.

@rintaro
Copy link
Member Author

rintaro commented Feb 23, 2023

@swift-ci Please smoke test

@rintaro rintaro merged commit 4190cb7 into swiftlang:main Feb 23, 2023
@rintaro rintaro deleted the macro-plugin-executable branch February 23, 2023 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants