Skip to content

Commit

Permalink
[WebAssembly] Mangle the argc/argv main as __wasm_argc_argv.
Browse files Browse the repository at this point in the history
WebAssembly enforces a rule that caller and callee signatures must
match. This means that the traditional technique of passing `main`
`argc` and `argv` even when it doesn't need them doesn't work.

Currently the backend renames `main` to `__original_main`, however this
doesn't interact well with LTO'ing libc, and the name isn't intuitive.
This patch allows us to transition to `__main_argc_argv` instead.

This implements the proposal in
WebAssembly/tool-conventions#134
with a flag to disable it when targeting Emscripten, though this is
expected to be temporary, as discussed in the proposal comments.

Differential Revision: https://reviews.llvm.org/D70700
  • Loading branch information
sunfishcode committed Feb 27, 2020
1 parent 197bda5 commit 00072c0
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 3 deletions.
19 changes: 18 additions & 1 deletion clang/lib/AST/Mangle.cpp
Expand Up @@ -50,7 +50,8 @@ enum CCMangling {
CCM_Fast,
CCM_RegCall,
CCM_Vector,
CCM_Std
CCM_Std,
CCM_WasmMainArgcArgv
};

static bool isExternC(const NamedDecl *ND) {
Expand All @@ -63,6 +64,16 @@ static CCMangling getCallingConvMangling(const ASTContext &Context,
const NamedDecl *ND) {
const TargetInfo &TI = Context.getTargetInfo();
const llvm::Triple &Triple = TI.getTriple();

// On wasm, the argc/argv form of "main" is renamed so that the startup code
// can call it with the correct function signature.
// On Emscripten, users may be exporting "main" and expecting to call it
// themselves, so we can't mangle it.
if (Triple.isWasm() && !Triple.isOSEmscripten())
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND))
if (FD->isMain() && FD->hasPrototype() && FD->param_size() == 2)
return CCM_WasmMainArgcArgv;

if (!Triple.isOSWindows() || !Triple.isX86())
return CCM_Other;

Expand Down Expand Up @@ -143,6 +154,12 @@ void MangleContext::mangleName(const NamedDecl *D, raw_ostream &Out) {

const ASTContext &ASTContext = getASTContext();
CCMangling CC = getCallingConvMangling(ASTContext, D);

if (CC == CCM_WasmMainArgcArgv) {
Out << "__main_argc_argv";
return;
}

bool MCXX = shouldMangleCXXName(D);
const TargetInfo &TI = Context.getTargetInfo();
if (CC == CCM_Other || (MCXX && TI.getCXXABI() == TargetCXXABI::Microsoft)) {
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Expand Up @@ -448,6 +448,10 @@ void CodeGenModule::Release() {
CodeGenFunction(*this).EmitCfiCheckStub();
}
emitAtAvailableLinkGuard();
if (Context.getTargetInfo().getTriple().isWasm() &&
!Context.getTargetInfo().getTriple().isOSEmscripten()) {
EmitMainVoidAlias();
}
emitLLVMUsed();
if (SanStats)
SanStats->finish();
Expand Down Expand Up @@ -5600,6 +5604,17 @@ void CodeGenModule::EmitDeferredUnusedCoverageMappings() {
}
}

void CodeGenModule::EmitMainVoidAlias() {
// In order to transition away from "__original_main" gracefully, emit an
// alias for "main" in the no-argument case so that libc can detect when
// new-style no-argument main is in used.
if (llvm::Function *F = getModule().getFunction("main")) {
if (!F->isDeclaration() && F->arg_size() == 0 && !F->isVarArg() &&
F->getReturnType()->isIntegerTy(Context.getTargetInfo().getIntWidth()))
addUsedGlobal(llvm::GlobalAlias::create("__main_void", F));
}
}

/// Turns the given pointer into a constant.
static llvm::Constant *GetPointerConstant(llvm::LLVMContext &Context,
const void *Ptr) {
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.h
Expand Up @@ -1023,6 +1023,9 @@ class CodeGenModule : public CodeGenTypeCache {
/// for the uninstrumented functions.
void EmitDeferredUnusedCoverageMappings();

/// Emit an alias for "main" if it has no arguments (needed for wasm).
void EmitMainVoidAlias();

/// Tell the consumer that this variable has been instantiated.
void HandleCXXStaticMemberVarInstantiation(VarDecl *VD);

Expand Down
3 changes: 1 addition & 2 deletions clang/lib/Frontend/InitHeaderSearch.cpp
Expand Up @@ -433,8 +433,7 @@ void InitHeaderSearch::AddDefaultIncludePaths(const LangOptions &Lang,
break;

case llvm::Triple::UnknownOS:
if (triple.getArch() == llvm::Triple::wasm32 ||
triple.getArch() == llvm::Triple::wasm64)
if (triple.isWasm())
return;
break;
}
Expand Down
13 changes: 13 additions & 0 deletions clang/test/CodeGen/wasm-call-main.c
@@ -0,0 +1,13 @@
// RUN: %clang_cc1 -triple wasm32 -o - -emit-llvm %s | FileCheck %s

// Mangle argc/argv main even when it's not defined in this TU.

#include <stddef.h>

int main(int argc, char *argv[]);

int foo(void) {
return main(0, NULL);
}

// CHECK: call i32 @__main_argc_argv(
9 changes: 9 additions & 0 deletions clang/test/CodeGen/wasm-main.c
@@ -0,0 +1,9 @@
// RUN: %clang_cc1 -triple wasm32 -o - -emit-llvm %s | FileCheck %s

// Don't mangle the no-arg form of main.

int main(void) {
return 0;
}

// CHECK-LABEL: define i32 @main()
9 changes: 9 additions & 0 deletions clang/test/CodeGen/wasm-main_argc_argv.c
@@ -0,0 +1,9 @@
// RUN: %clang_cc1 -triple wasm32 -o - -emit-llvm %s | FileCheck %s

// Mangle the argc/argv form of main.

int main(int argc, char **argv) {
return 0;
}

// CHECK-LABEL: define i32 @__main_argc_argv(i32 %argc, i8** %argv)

0 comments on commit 00072c0

Please sign in to comment.