Skip to content

Commit 00072c0

Browse files
committed
[WebAssembly] Mangle the argc/argv main as __wasm_argc_argv.
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
1 parent 197bda5 commit 00072c0

File tree

7 files changed

+68
-3
lines changed

7 files changed

+68
-3
lines changed

clang/lib/AST/Mangle.cpp

+18-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ enum CCMangling {
5050
CCM_Fast,
5151
CCM_RegCall,
5252
CCM_Vector,
53-
CCM_Std
53+
CCM_Std,
54+
CCM_WasmMainArgcArgv
5455
};
5556

5657
static bool isExternC(const NamedDecl *ND) {
@@ -63,6 +64,16 @@ static CCMangling getCallingConvMangling(const ASTContext &Context,
6364
const NamedDecl *ND) {
6465
const TargetInfo &TI = Context.getTargetInfo();
6566
const llvm::Triple &Triple = TI.getTriple();
67+
68+
// On wasm, the argc/argv form of "main" is renamed so that the startup code
69+
// can call it with the correct function signature.
70+
// On Emscripten, users may be exporting "main" and expecting to call it
71+
// themselves, so we can't mangle it.
72+
if (Triple.isWasm() && !Triple.isOSEmscripten())
73+
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND))
74+
if (FD->isMain() && FD->hasPrototype() && FD->param_size() == 2)
75+
return CCM_WasmMainArgcArgv;
76+
6677
if (!Triple.isOSWindows() || !Triple.isX86())
6778
return CCM_Other;
6879

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

144155
const ASTContext &ASTContext = getASTContext();
145156
CCMangling CC = getCallingConvMangling(ASTContext, D);
157+
158+
if (CC == CCM_WasmMainArgcArgv) {
159+
Out << "__main_argc_argv";
160+
return;
161+
}
162+
146163
bool MCXX = shouldMangleCXXName(D);
147164
const TargetInfo &TI = Context.getTargetInfo();
148165
if (CC == CCM_Other || (MCXX && TI.getCXXABI() == TargetCXXABI::Microsoft)) {

clang/lib/CodeGen/CodeGenModule.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,10 @@ void CodeGenModule::Release() {
448448
CodeGenFunction(*this).EmitCfiCheckStub();
449449
}
450450
emitAtAvailableLinkGuard();
451+
if (Context.getTargetInfo().getTriple().isWasm() &&
452+
!Context.getTargetInfo().getTriple().isOSEmscripten()) {
453+
EmitMainVoidAlias();
454+
}
451455
emitLLVMUsed();
452456
if (SanStats)
453457
SanStats->finish();
@@ -5600,6 +5604,17 @@ void CodeGenModule::EmitDeferredUnusedCoverageMappings() {
56005604
}
56015605
}
56025606

5607+
void CodeGenModule::EmitMainVoidAlias() {
5608+
// In order to transition away from "__original_main" gracefully, emit an
5609+
// alias for "main" in the no-argument case so that libc can detect when
5610+
// new-style no-argument main is in used.
5611+
if (llvm::Function *F = getModule().getFunction("main")) {
5612+
if (!F->isDeclaration() && F->arg_size() == 0 && !F->isVarArg() &&
5613+
F->getReturnType()->isIntegerTy(Context.getTargetInfo().getIntWidth()))
5614+
addUsedGlobal(llvm::GlobalAlias::create("__main_void", F));
5615+
}
5616+
}
5617+
56035618
/// Turns the given pointer into a constant.
56045619
static llvm::Constant *GetPointerConstant(llvm::LLVMContext &Context,
56055620
const void *Ptr) {

clang/lib/CodeGen/CodeGenModule.h

+3
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,9 @@ class CodeGenModule : public CodeGenTypeCache {
10231023
/// for the uninstrumented functions.
10241024
void EmitDeferredUnusedCoverageMappings();
10251025

1026+
/// Emit an alias for "main" if it has no arguments (needed for wasm).
1027+
void EmitMainVoidAlias();
1028+
10261029
/// Tell the consumer that this variable has been instantiated.
10271030
void HandleCXXStaticMemberVarInstantiation(VarDecl *VD);
10281031

clang/lib/Frontend/InitHeaderSearch.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,7 @@ void InitHeaderSearch::AddDefaultIncludePaths(const LangOptions &Lang,
433433
break;
434434

435435
case llvm::Triple::UnknownOS:
436-
if (triple.getArch() == llvm::Triple::wasm32 ||
437-
triple.getArch() == llvm::Triple::wasm64)
436+
if (triple.isWasm())
438437
return;
439438
break;
440439
}

clang/test/CodeGen/wasm-call-main.c

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %clang_cc1 -triple wasm32 -o - -emit-llvm %s | FileCheck %s
2+
3+
// Mangle argc/argv main even when it's not defined in this TU.
4+
5+
#include <stddef.h>
6+
7+
int main(int argc, char *argv[]);
8+
9+
int foo(void) {
10+
return main(0, NULL);
11+
}
12+
13+
// CHECK: call i32 @__main_argc_argv(

clang/test/CodeGen/wasm-main.c

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %clang_cc1 -triple wasm32 -o - -emit-llvm %s | FileCheck %s
2+
3+
// Don't mangle the no-arg form of main.
4+
5+
int main(void) {
6+
return 0;
7+
}
8+
9+
// CHECK-LABEL: define i32 @main()
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// RUN: %clang_cc1 -triple wasm32 -o - -emit-llvm %s | FileCheck %s
2+
3+
// Mangle the argc/argv form of main.
4+
5+
int main(int argc, char **argv) {
6+
return 0;
7+
}
8+
9+
// CHECK-LABEL: define i32 @__main_argc_argv(i32 %argc, i8** %argv)

0 commit comments

Comments
 (0)