StringRef Name;
uint8_t Kind;
uint32_t Flags;
- StringRef Module; // For undefined symbols the module name of the import
+ StringRef ImportModule; // For undefined symbols the module of the import
+ StringRef ImportName; // For undefined symbols the name of the import
union {
// For function or global symbols, the index in function or global index
// space.
const unsigned WASM_SYMBOL_VISIBILITY_DEFAULT = 0x0;
const unsigned WASM_SYMBOL_VISIBILITY_HIDDEN = 0x4;
const unsigned WASM_SYMBOL_UNDEFINED = 0x10;
+const unsigned WASM_SYMBOL_EXPLICIT_NAME = 0x40;
#define WASM_RELOC(name, value) name = value,
bool IsWeak = false;
bool IsHidden = false;
bool IsComdat = false;
- std::string ModuleName;
+ Optional<std::string> ImportModule;
+ Optional<std::string> ImportName;
wasm::WasmSignature *Signature = nullptr;
Optional<wasm::WasmGlobalType> GlobalType;
Optional<wasm::WasmEventType> EventType;
// Use a module name of "env" for now, for compatibility with existing tools.
// This is temporary, and may change, as the ABI is not yet stable.
MCSymbolWasm(const StringMapEntry<bool> *Name, bool isTemporary)
- : MCSymbol(SymbolKindWasm, Name, isTemporary), ModuleName("env") {}
+ : MCSymbol(SymbolKindWasm, Name, isTemporary) {}
static bool classof(const MCSymbol *S) { return S->isWasm(); }
const MCExpr *getSize() const { return SymbolSize; }
bool isComdat() const { return IsComdat; }
void setComdat(bool isComdat) { IsComdat = isComdat; }
- const StringRef getModuleName() const { return ModuleName; }
- void setModuleName(StringRef Name) { ModuleName = Name; }
+ const StringRef getImportModule() const {
+ if (ImportModule.hasValue()) {
+ return ImportModule.getValue();
+ }
+ return "env";
+ }
+ void setImportModule(StringRef Name) { ImportModule = Name; }
+
+ const StringRef getImportName() const {
+ if (ImportName.hasValue()) {
+ return ImportName.getValue();
+ }
+ return getName();
+ }
+ void setImportName(StringRef Name) { ImportName = Name; }
const wasm::WasmSignature *getSignature() const { return Signature; }
void setSignature(wasm::WasmSignature *Sig) { Signature = Sig; }
case wasm::WASM_SYMBOL_TYPE_GLOBAL:
case wasm::WASM_SYMBOL_TYPE_EVENT:
encodeULEB128(Sym.ElementIndex, W.OS);
- if ((Sym.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0)
+ if ((Sym.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0 ||
+ (Sym.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0)
writeString(Sym.Name);
break;
case wasm::WASM_SYMBOL_TYPE_DATA:
MCSymbolWasm *MemorySym =
cast<MCSymbolWasm>(Ctx.getOrCreateSymbol("__linear_memory"));
wasm::WasmImport MemImport;
- MemImport.Module = MemorySym->getModuleName();
- MemImport.Field = MemorySym->getName();
+ MemImport.Module = MemorySym->getImportModule();
+ MemImport.Field = MemorySym->getImportName();
MemImport.Kind = wasm::WASM_EXTERNAL_MEMORY;
Imports.push_back(MemImport);
MCSymbolWasm *TableSym =
cast<MCSymbolWasm>(Ctx.getOrCreateSymbol("__indirect_function_table"));
wasm::WasmImport TableImport;
- TableImport.Module = TableSym->getModuleName();
- TableImport.Field = TableSym->getName();
+ TableImport.Module = TableSym->getImportModule();
+ TableImport.Field = TableSym->getImportName();
TableImport.Kind = wasm::WASM_EXTERNAL_TABLE;
TableImport.Table.ElemType = wasm::WASM_TYPE_FUNCREF;
Imports.push_back(TableImport);
if (!WS.isDefined() && !WS.isComdat()) {
if (WS.isFunction()) {
wasm::WasmImport Import;
- Import.Module = WS.getModuleName();
- Import.Field = WS.getName();
+ Import.Module = WS.getImportModule();
+ Import.Field = WS.getImportName();
Import.Kind = wasm::WASM_EXTERNAL_FUNCTION;
Import.SigIndex = getFunctionType(WS);
Imports.push_back(Import);
report_fatal_error("undefined global symbol cannot be weak");
wasm::WasmImport Import;
- Import.Module = WS.getModuleName();
- Import.Field = WS.getName();
+ Import.Module = WS.getImportModule();
+ Import.Field = WS.getImportName();
Import.Kind = wasm::WASM_EXTERNAL_GLOBAL;
Import.Global = WS.getGlobalType();
Imports.push_back(Import);
report_fatal_error("undefined event symbol cannot be weak");
wasm::WasmImport Import;
- Import.Module = WS.getModuleName();
- Import.Field = WS.getName();
+ Import.Module = WS.getImportModule();
+ Import.Field = WS.getImportName();
Import.Kind = wasm::WASM_EXTERNAL_EVENT;
Import.Event.Attribute = wasm::WASM_EVENT_ATTRIBUTE_EXCEPTION;
Import.Event.SigIndex = getEventType(WS);
Flags |= wasm::WASM_SYMBOL_BINDING_LOCAL;
if (WS.isUndefined())
Flags |= wasm::WASM_SYMBOL_UNDEFINED;
+ if (WS.getName() != WS.getImportName())
+ Flags |= wasm::WASM_SYMBOL_EXPLICIT_NAME;
wasm::WasmSymbolInfo Info;
Info.Name = WS.getName();
Function.SymbolName = Info.Name;
} else {
wasm::WasmImport &Import = *ImportedFunctions[Info.ElementIndex];
+ if ((Info.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0)
+ Info.Name = readString(Ctx);
+ else
+ Info.Name = Import.Field;
Signature = &Signatures[Import.SigIndex];
- Info.Name = Import.Field;
- Info.Module = Import.Module;
+ Info.ImportName = Import.Field;
+ Info.ImportModule = Import.Module;
}
break;
Global.SymbolName = Info.Name;
} else {
wasm::WasmImport &Import = *ImportedGlobals[Info.ElementIndex];
- Info.Name = Import.Field;
+ if ((Info.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0)
+ Info.Name = readString(Ctx);
+ else
+ Info.Name = Import.Field;
GlobalType = &Import.Global;
+ Info.ImportName = Import.Field;
+ Info.ImportModule = Import.Module;
}
break;
} else {
wasm::WasmImport &Import = *ImportedEvents[Info.ElementIndex];
+ if ((Info.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0)
+ Info.Name = readString(Ctx);
+ else
+ Info.Name = Import.Field;
EventType = &Import.Event;
Signature = &Signatures[EventType->SigIndex];
- Info.Name = Import.Field;
+ Info.ImportName = Import.Field;
+ Info.ImportModule = Import.Module;
}
break;
}
}
void WebAssemblyTargetAsmStreamer::emitImportModule(const MCSymbolWasm *Sym,
- StringRef ModuleName) {
- OS << "\t.import_module\t" << Sym->getName() << ", " << ModuleName << '\n';
+ StringRef ImportModule) {
+ OS << "\t.import_module\t" << Sym->getName() << ", "
+ << ImportModule << '\n';
+}
+
+void WebAssemblyTargetAsmStreamer::emitImportName(const MCSymbolWasm *Sym,
+ StringRef ImportName) {
+ OS << "\t.import_name\t" << Sym->getName() << ", "
+ << ImportName << '\n';
}
void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) {
virtual void emitEventType(const MCSymbolWasm *Sym) = 0;
/// .import_module
virtual void emitImportModule(const MCSymbolWasm *Sym,
- StringRef ModuleName) = 0;
+ StringRef ImportModule) = 0;
+ /// .import_name
+ virtual void emitImportName(const MCSymbolWasm *Sym,
+ StringRef ImportName) = 0;
protected:
void emitValueType(wasm::ValType Type);
void emitIndIdx(const MCExpr *Value) override;
void emitGlobalType(const MCSymbolWasm *Sym) override;
void emitEventType(const MCSymbolWasm *Sym) override;
- void emitImportModule(const MCSymbolWasm *Sym, StringRef ModuleName) override;
+ void emitImportModule(const MCSymbolWasm *Sym, StringRef ImportModule) override;
+ void emitImportName(const MCSymbolWasm *Sym, StringRef ImportName) override;
};
/// This part is for Wasm object output
void emitGlobalType(const MCSymbolWasm *Sym) override {}
void emitEventType(const MCSymbolWasm *Sym) override {}
void emitImportModule(const MCSymbolWasm *Sym,
- StringRef ModuleName) override {}
+ StringRef ImportModule) override {}
+ void emitImportName(const MCSymbolWasm *Sym,
+ StringRef ImportName) override {}
};
/// This part is for null output
void emitGlobalType(const MCSymbolWasm *) override {}
void emitEventType(const MCSymbolWasm *) override {}
void emitImportModule(const MCSymbolWasm *, StringRef) override {}
+ void emitImportName(const MCSymbolWasm *, StringRef) override {}
};
} // end namespace llvm
F.hasFnAttribute("wasm-import-module")) {
StringRef Name =
F.getFnAttribute("wasm-import-module").getValueAsString();
- Sym->setModuleName(Name);
+ Sym->setImportModule(Name);
getTargetStreamer()->emitImportModule(Sym, Name);
}
+ if (TM.getTargetTriple().isOSBinFormatWasm() &&
+ F.hasFnAttribute("wasm-import-name")) {
+ StringRef Name =
+ F.getFnAttribute("wasm-import-name").getValueAsString();
+ Sym->setImportName(Name);
+ getTargetStreamer()->emitImportName(Sym, Name);
+ }
}
}
#define DEBUG_TYPE "wasm-fix-function-bitcasts"
-static cl::opt<bool>
- TemporaryWorkarounds("wasm-temporary-workarounds",
- cl::desc("Apply certain temporary workarounds"),
- cl::init(true), cl::Hidden);
-
namespace {
class FixFunctionBitcasts final : public ModulePass {
StringRef getPassName() const override {
return Wrapper;
}
+// Test whether a main function with type FuncTy should be rewritten to have
+// type MainTy.
+bool shouldFixMainFunction(FunctionType *FuncTy, FunctionType *MainTy) {
+ // Only fix the main function if it's the standard zero-arg form. That way,
+ // the standard cases will work as expected, and users will see signature
+ // mismatches from the linker for non-standard cases.
+ return FuncTy->getReturnType() == MainTy->getReturnType() &&
+ FuncTy->getNumParams() == 0 &&
+ !FuncTy->isVarArg();
+}
+
bool FixFunctionBitcasts::runOnModule(Module &M) {
LLVM_DEBUG(dbgs() << "********** Fix Function Bitcasts **********\n");
// "int main(int argc, char *argv[])", create an artificial call with it
// bitcasted to that type so that we generate a wrapper for it, so that
// the C runtime can call it.
- if (!TemporaryWorkarounds && !F.isDeclaration() && F.getName() == "main") {
+ if (F.getName() == "main") {
Main = &F;
LLVMContext &C = M.getContext();
Type *MainArgTys[] = {Type::getInt32Ty(C),
PointerType::get(Type::getInt8PtrTy(C), 0)};
FunctionType *MainTy = FunctionType::get(Type::getInt32Ty(C), MainArgTys,
/*isVarArg=*/false);
- if (F.getFunctionType() != MainTy) {
+ if (shouldFixMainFunction(F.getFunctionType(), MainTy)) {
LLVM_DEBUG(dbgs() << "Found `main` function with incorrect type: "
<< *F.getFunctionType() << "\n");
Value *Args[] = {UndefValue::get(MainArgTys[0]),
Main->setName("__original_main");
Function *MainWrapper =
cast<Function>(CallMain->getCalledValue()->stripPointerCasts());
- MainWrapper->setName("main");
- MainWrapper->setLinkage(Main->getLinkage());
- MainWrapper->setVisibility(Main->getVisibility());
- Main->setLinkage(Function::PrivateLinkage);
- Main->setVisibility(Function::DefaultVisibility);
delete CallMain;
+ if (Main->isDeclaration()) {
+ // The wrapper is not needed in this case as we don't need to export
+ // it to anyone else.
+ MainWrapper->eraseFromParent();
+ } else {
+ // Otherwise give the wrapper the same linkage as the original main
+ // function, so that it can be called from the same places.
+ MainWrapper->setName("main");
+ MainWrapper->setLinkage(Main->getLinkage());
+ MainWrapper->setVisibility(Main->getVisibility());
+ }
}
return true;
-; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -wasm-temporary-workarounds=false -mattr=+sign-ext,+simd128 | FileCheck %s
-; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -fast-isel -fast-isel-abort=1 -wasm-temporary-workarounds=false -mattr=+sign-ext,+simd128 | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -mattr=+sign-ext,+simd128 | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -fast-isel -fast-isel-abort=1 -mattr=+sign-ext,+simd128 | FileCheck %s
; Test that basic call operations assemble as expected.
-; RUN: llc < %s -asm-verbose=false -wasm-temporary-workarounds=false -wasm-keep-registers | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -wasm-keep-registers | FileCheck %s
; Test that function pointer casts casting away varargs are replaced with
; wrappers.
-; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers -enable-emscripten-cxx-exceptions -wasm-temporary-workarounds=false | FileCheck %s
+; RUN: llc < %s -asm-verbose=false -wasm-disable-explicit-locals -wasm-keep-registers -enable-emscripten-cxx-exceptions | FileCheck %s
; Test that function pointer casts are replaced with wrappers.
declare void @foo() #0
declare void @plain()
-attributes #0 = { "wasm-import-module"="bar" }
+attributes #0 = { "wasm-import-module"="bar" "wasm-import-name"="qux" }
; CHECK-NOT: .import_module plain
; CHECK: .import_module foo, bar
+; CHECK: .import_name foo, qux
; CHECK-NOT: .import_module plain
-; RUN: llc < %s -asm-verbose=false -wasm-temporary-workarounds=false | FileCheck %s
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
; Test main functions with alternate signatures.
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
-declare void @main()
+declare i32 @main()
-define void @foo() {
- call void @main()
- ret void
+define i32 @foo() {
+ %t = call i32 @main()
+ ret i32 %t
}
-; CHECK-NOT: __original_main
; CHECK-LABEL: foo:
-; CHECK-NEXT: .functype foo () -> ()
-; CHECK-NEXT: call main@FUNCTION
+; CHECK-NEXT: .functype foo () -> (i32)
+; CHECK-NEXT: call __original_main@FUNCTION
; CHECK-NEXT: end_function
-; CHECK-NOT: __original_main
-; RUN: llc < %s -asm-verbose=false -wasm-temporary-workarounds=false | FileCheck %s
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
; Test main functions with alternate signatures.
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
-define void @main() {
- ret void
+define i32 @main() {
+ ret i32 0
}
-; CHECK-LABEL: .L__original_main:
-; CHECK-NEXT: .functype .L__original_main () -> ()
+; CHECK-LABEL: __original_main:
+; CHECK-NEXT: .functype __original_main () -> (i32)
+; CHECK-NEXT: i32.const 0
; CHECK-NEXT: end_function
; CHECK-LABEL: main:
; CHECK-NEXT: .functype main (i32, i32) -> (i32)
-; CHECK: call .L__original_main@FUNCTION
+; CHECK: call __original_main@FUNCTION
--- /dev/null
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
+
+; Test that main function with a non-standard third argument is
+; not wrapped.
+
+target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
+target triple = "wasm32-unknown-unknown"
+
+define i32 @main(i32 %a, i8** %b, i8** %c) {
+ ret i32 0
+}
+
+; CHECK-LABEL: main:
+; CHECK-NEXT: .functype main (i32, i32, i32) -> (i32)
+
+; CHECK-NOT: __original_main:
-; RUN: llc < %s -asm-verbose=false -wasm-temporary-workarounds=false | FileCheck %s
+; RUN: llc < %s -asm-verbose=false | FileCheck %s
; Test that main function with expected signature is not wrapped
declare void @f0(i32) #0
@ptr_to_f0 = hidden global void (i32)* @f0, align 4
-attributes #0 = { "wasm-import-module"="somewhere" }
+attributes #0 = { "wasm-import-module"="somewhere" "wasm-import-name"="something" }
declare void @f1(i32) #1
@ptr_to_f1 = hidden global void (i32)* @f1, align 4
; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: SigIndex: 1
; CHECK: - Module: somewhere
-; CHECK-NEXT: Field: f0
+; CHECK-NEXT: Field: something
; CHECK: - Module: env
; CHECK-NEXT: Field: f1
; CHECK-NEXT: Kind: FUNCTION
--- /dev/null
+; RUN: llc -filetype=obj %s -o - | obj2yaml | FileCheck %s
+
+target triple = "wasm32-unknown-unknown"
+
+define void @test() {
+ call void @foo()
+ call void @plain()
+ ret void
+}
+
+declare void @foo() #0
+declare void @plain()
+
+attributes #0 = { "wasm-import-module"="bar" "wasm-import-name"="qux" }
+
+; CHECK: - Type: IMPORT
+; CHECK-NEXT: Imports:
+; CHECK: - Module: bar
+; CHECK-NEXT: Field: qux
+; CHECK-NEXT: Kind: FUNCTION
+
+; CHECK: - Module: env
+; CHECK-NEXT: Field: plain
+; CHECK-NEXT: Kind: FUNCTION
+
+; CHECK: - Type: CUSTOM
+; CHECK: Name: foo
+; CHECK-NEXT: Flags: [ UNDEFINED ]
+
+; CHECK: Name: plain
+; CHECK-NEXT: Flags: [ UNDEFINED ]
case wasm::WASM_SYMBOL_TYPE_GLOBAL:
case wasm::WASM_SYMBOL_TYPE_EVENT:
encodeULEB128(Info.ElementIndex, SubSection.GetStream());
- if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0)
+ if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0 ||
+ (Info.Flags & wasm::WASM_SYMBOL_EXPLICIT_NAME) != 0)
writeStringRef(Info.Name, SubSection.GetStream());
break;
case wasm::WASM_SYMBOL_TYPE_DATA: