+.functype ret32 (f32) -> (i32)
+
.globl call_ret32
call_ret32:
.functype call_ret32 () -> (i32)
ret32_address:
.int32 ret32
.size ret32_address, 4
-
- .functype ret32 (f32) -> (i32)
+ .functype def () -> ()
+
.section .text.lib_func,"",@
.globl lib_func
lib_func:
# Like Inputs/ctor-setup.s, except it calls `def` instead of `lib_func`,
# so it pulls in the .o file containing `ctor`.
+.functype def () -> ()
+
.section .text._start,"",@
.globl _start
_start:
.section .init_array,"",@
.p2align 2
.int32 setup
-
-.functype def () -> ()
# Like Inputs/ctor-start.s, except it calls `lib_func` from a ctor
# instead of from `_start`.
+.functype lib_func () -> ()
+
.globl _start
_start:
.functype _start () -> ()
.p2align 2
.int32 setup
- .functype lib_func () -> ()
+.functype lib_func () -> ()
+
.globl _start
_start:
.functype _start () -> ()
call lib_func
end_function
- .functype lib_func () -> ()
# void puts(const char*);
# void hello() { puts("hello\n"); }
+ .functype puts (i32) -> ()
+
.globl hello
hello:
.functype hello () -> ()
hello_str:
.asciz "hello\n"
.size hello_str, 7
-
- .functype puts (i32) -> ()
+.functype foo () -> ()
+
.globl call_foo
call_foo:
.functype call_foo () -> ()
call foo
end_function
- .functype foo () -> ()
.import_module foo, baz
.globl call_foo
call_foo:
.functype call_foo () -> (i32)
+ f32.const 0.0
call foo
end_function
.globl unused_undef_global
.globl used_undef_global
+.globaltype unused_undef_global, i64, immutable
+.globaltype used_undef_global, i64, immutable
+
use_undef_global:
.functype use_undef_global () -> (i64)
global.get used_undef_global
end_function
-
-.globaltype unused_undef_global, i64, immutable
-.globaltype used_undef_global, i64, immutable
// SYMBOLIC-NEXT: - Type: EXPORT
.globl foo
+
foo:
.functype foo () -> ()
end_function
# RUN: wasm-ld -o %t.wasm %t.o %t1.o %t2.o
# RUN: obj2yaml %t.wasm | FileCheck %s
+ .functype foo () -> ()
.globl _start
.type _start,@function
call foo
end_function
- .functype foo () -> ()
-
# Check that we got 1 copy of each of the .debug_foo sections from the 2 object
# files, and that they came from the same object.
# RUN: wasm-ld %t.o -o %t.wasm
# RUN: obj2yaml %t.wasm | FileCheck %s
+ .functype __wasm_call_ctors () -> ()
+
.globl myctor
myctor:
.functype myctor () -> (i32)
# foo which is not otherwise used and will not be marked a live in the output.
# Verify the tombstone value is written to debug_info section.
+.globaltype foo, i32
+
.globl _start
_start:
.functype _start () -> ()
.section .debug_info,"",@
.int32 foo
-.globaltype foo, i32
foo:
# CHECK: - Type: CUSTOM
# CHECK-NODEMANGLE: error: {{.*}}.o: undefined symbol: _Z3fooi
+.functype _Z3fooi (i32) -> ()
+
.globl _start
_start:
.functype _start () -> ()
call _Z3fooi
end_function
-.functype _Z3fooi (i32) -> ()
# RUN: not wasm-ld %t.o -o / 2>&1 | FileCheck %s -check-prefixes=ROOT,CHECK
# ROOT: error: cannot open output file /
+.functype undefined_symbol () -> ()
+
_start:
.functype _start () -> ()
call undefined_symbol
.globl _start
_start:
.functype _start (i64) -> (f32)
+ f32.const 0.0
end_function
# RUN: wasm-ld --export-all -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s
+.globaltype __stack_pointer, i32
+
.globl _start
_start:
i32.const 42
end_function
-.globaltype __stack_pointer, i32
-
# CHECK: - Type: EXPORT
# CHECK-NEXT: Exports:
# CHECK-NEXT: - Name: memory
.globl _start
_start:
.functype _start () -> ()
+ i32.const 0
i32.load foo
drop
end_function
.functype use_undef_global () -> (i64)
foo:
- .functype foo () -> ()
+ .functype foo (i64) -> (i64)
+ local.get 0
call unused_undef_function
end_function
_start:
.functype _start () -> ()
call used_undef_function
+ drop
call use_undef_global
+ drop
end_function
# RUN: obj2yaml %t1.wasm | FileCheck %s
# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown %S/Inputs/import-attributes.s -o %t2.o
# RUN: not wasm-ld --export call_foo --allow-undefined -o %t.wasm %t1.o %t2.o 2>&1 | FileCheck %s
+.functype foo () -> ()
+
.globl _start
_start:
.functype _start () -> ()
call foo
end_function
-.functype foo () -> ()
.import_module foo, bar
# CHECK: wasm-ld: error: import module mismatch for symbol: foo
wasm_global:
bar:
- .functype bar () -> ()
+ .functype bar () -> (i32)
i32.const somedata
i32.const somezeroes
drop
- drop
end_function
write_global:
# CHECK: Addr Off Size Out In Symbol
-# CHECK-NEXT: - 8 a TYPE
-# CHECK-NEXT: - 12 6 FUNCTION
-# CHECK-NEXT: - 18 7 TABLE
-# CHECK-NEXT: - 1f 5 MEMORY
-# CHECK-NEXT: - 24 f GLOBAL
+# CHECK-NEXT: - 8 e TYPE
+# CHECK-NEXT: - 16 6 FUNCTION
+# CHECK-NEXT: - 1c 7 TABLE
+# CHECK-NEXT: - 23 5 MEMORY
+# CHECK-NEXT: - 28 f GLOBAL
# CHECK-NEXT: 0 0 0 __stack_pointer
# CHECK-NEXT: 1 0 0 wasm_global
-# CHECK-NEXT: - 33 15 EXPORT
-# CHECK-NEXT: - 48 2e CODE
-# CHECK-NEXT: - 49 11 {{.*}}{{/|\\}}map-file.s.tmp1.o:(bar)
-# CHECK-NEXT: - 49 11 bar
-# CHECK-NEXT: - 5a b {{.*}}{{/|\\}}map-file.s.tmp1.o:(write_global)
-# CHECK-NEXT: - 5a b write_global
-# CHECK-NEXT: - 65 f {{.*}}{{/|\\}}map-file.s.tmp1.o:(_start)
-# CHECK-NEXT: - 65 f _start
-# CHECK-NEXT: - 76 d DATA
-# CHECK-NEXT: 400 77 4 .data
-# CHECK-NEXT: 400 7d 4 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.data.somedata)
-# CHECK-NEXT: 400 7d 4 somedata
-# CHECK-NEXT: 404 76 4 .bss
+# CHECK-NEXT: - 37 15 EXPORT
+# CHECK-NEXT: - 4c 2d CODE
+# CHECK-NEXT: - 4d 10 {{.*}}{{/|\\}}map-file.s.tmp1.o:(bar)
+# CHECK-NEXT: - 4d 10 bar
+# CHECK-NEXT: - 5d b {{.*}}{{/|\\}}map-file.s.tmp1.o:(write_global)
+# CHECK-NEXT: - 5d b write_global
+# CHECK-NEXT: - 68 f {{.*}}{{/|\\}}map-file.s.tmp1.o:(_start)
+# CHECK-NEXT: - 68 f _start
+# CHECK-NEXT: - 79 d DATA
+# CHECK-NEXT: 400 7a 4 .data
+# CHECK-NEXT: 400 80 4 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.data.somedata)
+# CHECK-NEXT: 400 80 4 somedata
+# CHECK-NEXT: 404 79 4 .bss
# CHECK-NEXT: 404 0 4 {{.*}}{{/|\\}}map-file.s.tmp1.o:(.bss.somezeroes)
# CHECK-NEXT: 404 0 4 somezeroes
-# CHECK-NEXT: - 83 12 CUSTOM(.debug_info)
-# CHECK-NEXT: - 95 50 CUSTOM(name)
+# CHECK-NEXT: - 86 12 CUSTOM(.debug_info)
+# CHECK-NEXT: - 98 50 CUSTOM(name)
# RUN: not wasm-ld %t1.o -o /dev/null -Map=/ 2>&1 \
# RUN: | FileCheck -check-prefix=FAIL %s
.globl fn_i64_f64_i32_f32
-_start:
- .functype _start () -> ()
- call fn_i32
- drop
- call fn_i32_i32
- drop
- drop
- call fn_i32_i64
- drop
- drop
- call fn_i64_f64_i32_f32
- drop
- drop
- drop
- drop
- end_function
-
fn_i32:
.functype fn_i32 () -> (i32)
i32.const 1
f32.const 1.0
end_function
+_start:
+ .functype _start () -> ()
+ call fn_i32
+ drop
+ call fn_i32_i32
+ drop
+ drop
+ call fn_i32_i64
+ drop
+ drop
+ call fn_i64_f64_i32_f32
+ drop
+ drop
+ drop
+ drop
+ end_function
+
# CHECK: - Type: TYPE
# CHECK-NEXT: Signatures:
# RUN: not wasm-ld %t.o -o %t.wasm 2>&1 | FileCheck %s
# RUN: wasm-ld --features=mutable-globals %t.o -o %t.wasm
+.globaltype foo, i32
+
.globl _start
_start:
.functype _start () -> ()
global.set foo
end_function
-.globaltype foo, i32
.import_module foo, env
.import_name foo, foo
.globl _Z3fooi
.weak _Z3bari
+.functype _Z3bari (i32) -> ()
+
+_Z3fooi:
+ .functype _Z3fooi (i32) -> ()
+ end_function
+
_start:
.functype _start () -> ()
i32.const 1
call _Z3bari
end_function
-_Z3fooi:
- .functype _Z3fooi (i32) -> ()
- end_function
-
-.functype _Z3bari (i32) -> ()
-
# CHECK: - Type: EXPORT
# CHECK-NEXT: Exports:
# CHECK-NEXT: - Name: memory
.type _start,@function
_start:
.functype _start () -> ()
+ i32.const 0
i32.load foo
+ drop
end_function
.functype _start () -> ()
i32.const foo@TLSREL
i32.const bar@TLSREL
+ drop
+ drop
end_function
.section .data,"",@
# RUN: wasm-ld --experimental-pic -shared -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s
+.functype func_external () -> ()
+
+# Linker-synthesized globals
+.globaltype __stack_pointer, i32
+.globaltype __table_base, i32, immutable
+.globaltype __memory_base, i32, immutable
+
.section .data.data,"",@
data:
.p2align 2
.int8 15
.ascii "mutable-globals"
-.functype func_external () -> ()
-
-# Linker-synthesized globals
-.globaltype __stack_pointer, i32
-.globaltype __table_base, i32, immutable
-.globaltype __memory_base, i32, immutable
-
# check for dylink section at start
# CHECK: Sections:
# RUN: wasm-ld -mwasm64 --experimental-pic -shared -o %t.wasm %t.o
# RUN: obj2yaml %t.wasm | FileCheck %s
+.functype func_external () -> ()
+
+# Linker-synthesized globals
+.globaltype __stack_pointer, i64
+.globaltype __table_base, i64, immutable
+.globaltype __memory_base, i64, immutable
+
.section .data.data,"",@
data:
.p2align 2
.section .data.data_addr_external,"",@
data_addr_external:
- .int32 data_external
- .size data_addr_external, 4
+ .int64 data_external
+ .size data_addr_external, 8
# .. including addends
end_function
get_func_address:
- .functype get_func_address () -> (i32)
+ .functype get_func_address () -> (i64)
global.get func_external@GOT
end_function
get_data_address:
- .functype get_data_address () -> (i32)
+ .functype get_data_address () -> (i64)
global.get data_external@GOT
end_function
.int8 15
.ascii "mutable-globals"
-.functype func_external () -> ()
-
-# Linker-synthesized globals
-.globaltype __stack_pointer, i64
-.globaltype __table_base, i64, immutable
-.globaltype __memory_base, i64, immutable
-
# check for dylink section at start
# CHECK: Sections:
# CHECK-NEXT: - Type: CUSTOM
# CHECK-NEXT: Name: dylink
-# CHECK-NEXT: MemorySize: 24
+# CHECK-NEXT: MemorySize: 28
# CHECK-NEXT: MemoryAlignment: 2
# CHECK-NEXT: TableSize: 2
# CHECK-NEXT: TableAlignment: 0
# CHECK-NEXT: Body: 10020B
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Locals: []
-# CHECK-NEXT: Body: 230142047C2305360200230142087C230241016A3602002301420C7C230141006A360200230142107C2306360200230142147C230741046A3602000B
+# CHECK-NEXT: Body: 230142047C2305360200230142087C230241016A3602002301420C7C230141006A360200230142107C2306370200230142187C230741046A3602000B
# check the data segment initialized with __memory_base global as offset
# CHECK-NEXT: Offset:
# CHECK-NEXT: Opcode: GLOBAL_GET
# CHECK-NEXT: Index: 1
-# CHECK-NEXT: Content: '020000000000000001000000000000000000000000000000'
+# CHECK-NEXT: Content: '02000000000000000100000000000000000000000000000000000000'
# function was seen first and the defined function was referenced within the
# the defining file (see %S/Inputs/sig_mismatch.s).
+.functype foo (i32, i64, i32) -> (i32)
+
.globl _start
_start:
.functype _start () -> ()
i64.const 2
i32.const 3
call foo
+ drop
end_function
-.functype foo (i32, i64, i32) -> (i32)
-
# CHECK: - Type: CUSTOM
# CHECK-NEXT: Name: linking
# CHECK-NEXT: Version: 2
.globl _start
_start:
.functype _start () -> (i32)
+ i32.const 0
i32.load data_external
end_function
# Check that calling an undefined weak function generates an appropriate stub
# that will fail at runtime with "unreachable".
+.functype weakFunc1 () -> ()
+.functype weakFunc2 () -> ()
+.functype weakFunc3 (i32) -> ()
+.functype weakFunc4 () -> ()
+
.globl callWeakFuncs
callWeakFuncs:
.weak weakFunc2
.weak weakFunc3
.weak weakFunc4
-.functype weakFunc1 () -> ()
-.functype weakFunc2 () -> ()
-.functype weakFunc3 (i32) -> ()
-.functype weakFunc4 () -> ()
# CHECK-GC: removing unused section {{.*}}:(weakFunc4)
# IGNORE-NEXT: Body: 000B
# IGNORE-NEXT: - Index: 1
# IGNORE-NEXT: Locals: []
-# IGNORE-NEXT: Body: 1080808080001082808080001083808080000B
+# IGNORE-NEXT: Body: 1080808080001082808080001083808080001A1A0B
# IGNORE-NEXT: - Index: 2
# IGNORE-NEXT: Locals: []
# IGNORE-NEXT: Body: 4180808080000F0B
# RUN: wasm-ld -r %t1.o -o %t4.wasm --unresolved-symbols=report-all
# RUN: llvm-readobj %t4.wasm > /dev/null 2>&1
+.functype undef_func () -> ()
+.functype get_data_addr () -> (i32)
+.functype get_func_addr () -> (i32)
+
.globl _start
_start:
.functype _start () -> ()
call undef_func
call get_data_addr
call get_func_addr
+ drop
+ drop
end_function
.globl get_data_addr
i32.const undef_func
return
end_function
-
-.functype undef_func () -> ()
# CHECK: undefined symbol: foo
+.functype foo () -> ()
+
_start:
.globl _start
.functype _start () -> ()
call foo
end_function
-
-.functype foo () -> ()
# RUN: wasm-ld --export-dynamic -o %t.wasm %t.o %t1.o %t2.o
# RUN: obj2yaml %t.wasm | FileCheck %s
+.functype weakFn () -> (i32)
+
.globl _start
_start:
.functype _start () -> ()
drop
end_function
-.functype weakFn () -> (i32)
.size weakGlobal, 4
# CHECK: --- !WASM
# RUN: wasm-ld --unresolved-symbols=ignore-all %t.o -o %t2.wasm
# RUN: obj2yaml %t2.wasm | FileCheck %s
+.functype foo () -> (i32)
+
.globl get_foo_addr
get_foo_addr:
.functype get_foo_addr () -> (i32)
.globl _start
_start:
- .functype _start () -> ()
+ .functype _start () -> (i32)
call get_foo_addr
call foo
+ drop
end_function
.weak foo
-.functype foo () -> (i32)
# Verify that we do not generate dynamic relocations for the GOT entry.
# Test that undefined weak externals (global_var) and (foo) don't cause
# link failures and resolve to zero.
+.functype foo () -> (i32)
+
.globl get_address_of_foo
get_address_of_foo:
.functype get_address_of_foo () -> (i32)
.weak foo
.weak global_var
-.functype foo () -> (i32)
# CHECK: --- !WASM
/// Machine Function map.
void deleteMachineFunctionFor(Function &F);
- /// Keep track of various per-function pieces of information for backends
+ /// Keep track of various per-module pieces of information for backends
/// that would like to do so.
template<typename Ty>
Ty &getObjFileInfo() {
#define LLVM_CODEGEN_MACHINEMODULEINFOIMPLS_H
#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include <cassert>
SymbolListTy GetGVStubList() { return getSortedStubs(GVStubs); }
};
+/// MachineModuleInfoWasm - This is a MachineModuleInfoImpl implementation
+/// for Wasm targets.
+class MachineModuleInfoWasm : public MachineModuleInfoImpl {
+ virtual void anchor(); // Out of line virtual method.
+
+public:
+ MachineModuleInfoWasm(const MachineModuleInfo &) {}
+
+ StringSet<> MachineSymbolsUsed;
+};
+
} // end namespace llvm
#endif // LLVM_CODEGEN_MACHINEMODULEINFOIMPLS_H
bool MCFatalWarnings : 1;
bool MCNoWarn : 1;
bool MCNoDeprecatedWarn : 1;
+ bool MCNoTypeCheck : 1;
bool MCSaveTempLabels : 1;
bool MCUseDwarfDirectory : 1;
bool MCIncrementalLinkerCompatible : 1;
bool getNoDeprecatedWarn();
+bool getNoTypeCheck();
+
std::string getABIName();
/// Create this object with static storage to register mc-related command
void MachineModuleInfoMachO::anchor() {}
void MachineModuleInfoELF::anchor() {}
void MachineModuleInfoCOFF::anchor() {}
+void MachineModuleInfoWasm::anchor() {}
using PairTy = std::pair<MCSymbol *, MachineModuleInfoImpl::StubValueTy>;
static int SortSymbolPair(const PairTy *LHS, const PairTy *RHS) {
MCTargetOptions::MCTargetOptions()
: MCRelaxAll(false), MCNoExecStack(false), MCFatalWarnings(false),
- MCNoWarn(false), MCNoDeprecatedWarn(false), MCSaveTempLabels(false),
+ MCNoWarn(false), MCNoDeprecatedWarn(false),
+ MCNoTypeCheck(false), MCSaveTempLabels(false),
MCUseDwarfDirectory(false), MCIncrementalLinkerCompatible(false),
ShowMCEncoding(false), ShowMCInst(false), AsmVerbose(false),
PreserveAsmComments(true), Dwarf64(false) {}
MCOPT(bool, FatalWarnings)
MCOPT(bool, NoWarn)
MCOPT(bool, NoDeprecatedWarn)
+MCOPT(bool, NoTypeCheck)
MCOPT(std::string, ABIName)
llvm::mc::RegisterMCTargetOptionsFlags::RegisterMCTargetOptionsFlags() {
"no-deprecated-warn", cl::desc("Suppress all deprecated warnings"));
MCBINDOPT(NoDeprecatedWarn);
+ static cl::opt<bool> NoTypeCheck(
+ "no-type-check", cl::desc("Suppress type errors (Wasm)"));
+ MCBINDOPT(NoTypeCheck);
+
static cl::opt<std::string> ABIName(
"target-abi", cl::Hidden,
cl::desc("The name of the ABI to be targeted from the backend."),
Options.MCFatalWarnings = getFatalWarnings();
Options.MCNoWarn = getNoWarn();
Options.MCNoDeprecatedWarn = getNoDeprecatedWarn();
+ Options.MCNoTypeCheck = getNoTypeCheck();
return Options;
}
add_llvm_component_library(LLVMWebAssemblyAsmParser
WebAssemblyAsmParser.cpp
+ WebAssemblyAsmTypeCheck.cpp
LINK_COMPONENTS
MC
///
//===----------------------------------------------------------------------===//
+#include "AsmParser/WebAssemblyAsmTypeCheck.h"
#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
#include "TargetInfo/WebAssemblyTargetInfo.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCSymbolWasm.h"
#include "llvm/Support/Endian.h"
+#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
using namespace llvm;
Else,
Undefined,
};
- std::vector<NestingType> NestingStack;
+ struct Nested {
+ NestingType NT;
+ wasm::WasmSignature Sig;
+ };
+ std::vector<Nested> NestingStack;
MCSymbolWasm *DefaultFunctionTable = nullptr;
MCSymbol *LastFunctionLabel = nullptr;
+ bool is64;
+
+ WebAssemblyAsmTypeCheck TC;
+ // Don't type check if -no-type-check was set.
+ bool SkipTypeCheck;
+
public:
WebAssemblyAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
const MCInstrInfo &MII, const MCTargetOptions &Options)
: MCTargetAsmParser(Options, STI, MII), Parser(Parser),
- Lexer(Parser.getLexer()) {
+ Lexer(Parser.getLexer()),
+ is64(STI.getTargetTriple().isArch64Bit()),
+ TC(Parser, MII, is64), SkipTypeCheck(Options.MCNoTypeCheck) {
setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
+ // Don't type check if this is inline asm, since that is a naked sequence of
+ // instructions without a function/locals decl.
+ auto &SM = Parser.getSourceManager();
+ auto BufferName =
+ SM.getBufferInfo(SM.getMainFileID()).Buffer->getBufferIdentifier();
+ if (BufferName == "<inline asm>")
+ SkipTypeCheck = true;
}
void Initialize(MCAsmParser &Parser) override {
}
}
- void push(NestingType NT) { NestingStack.push_back(NT); }
+ void push(NestingType NT) { NestingStack.push_back({NT}); }
bool pop(StringRef Ins, NestingType NT1, NestingType NT2 = Undefined) {
if (NestingStack.empty())
return error(Twine("End of block construct with no start: ") + Ins);
auto Top = NestingStack.back();
- if (Top != NT1 && Top != NT2)
+ if (Top.NT != NT1 && Top.NT != NT2)
return error(Twine("Block construct type mismatch, expected: ") +
- nestingString(Top).second + ", instead got: " + Ins);
+ nestingString(Top.NT).second + ", instead got: " + Ins);
+ TC.setLastSig(Top.Sig);
NestingStack.pop_back();
return false;
}
auto Err = !NestingStack.empty();
while (!NestingStack.empty()) {
error(Twine("Unmatched block construct(s) at function end: ") +
- nestingString(NestingStack.back()).first);
+ nestingString(NestingStack.back().NT).first);
NestingStack.pop_back();
}
return Err;
void addBlockTypeOperand(OperandVector &Operands, SMLoc NameLoc,
WebAssembly::BlockType BT) {
+ if (BT != WebAssembly::BlockType::Void) {
+ wasm::WasmSignature Sig({static_cast<wasm::ValType>(BT)}, {});
+ TC.setLastSig(Sig);
+ NestingStack.back().Sig = Sig;
+ }
Operands.push_back(std::make_unique<WebAssemblyOperand>(
WebAssemblyOperand::Integer, NameLoc, NameLoc,
WebAssemblyOperand::IntOp{static_cast<int64_t>(BT)}));
return true;
// Got signature as block type, don't need more
ExpectBlockType = false;
+ TC.setLastSig(*Signature.get());
+ if (ExpectBlockType)
+ NestingStack.back().Sig = *Signature.get();
auto &Ctx = getContext();
// The "true" here will cause this to be a nameless symbol.
MCSymbol *Sym = Ctx.createTempSymbol("typeindex", true);
auto Signature = std::make_unique<wasm::WasmSignature>();
if (parseSignature(Signature.get()))
return true;
+ TC.funcDecl(*Signature);
WasmSym->setSignature(Signature.get());
addSignature(std::move(Signature));
WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
SmallVector<wasm::ValType, 4> Locals;
if (parseRegTypeList(Locals))
return true;
+ TC.localDecl(Locals);
TOut.emitLocal(Locals);
CurrentState = FunctionLocals;
return expect(AsmToken::EndOfStatement, "EOL");
if (Op0.getImm() == -1)
Op0.setImm(Align);
}
- if (getSTI().getTargetTriple().isArch64Bit()) {
+ if (is64) {
// Upgrade 32-bit loads/stores to 64-bit. These mostly differ by having
// an offset64 arg instead of offset32, but to the assembler matcher
// they're both immediates so don't get selected for.
Inst.setOpcode(Opc64);
}
}
+ if (!SkipTypeCheck && TC.typeCheck(IDLoc, Inst))
+ return true;
Out.emitInstruction(Inst, getSTI());
if (CurrentState == EndFunction) {
- onEndOfFunction();
+ onEndOfFunction(IDLoc);
} else {
CurrentState = Instructions;
}
getContext().addGenDwarfSection(WS);
}
- void onEndOfFunction() {
+ void onEndOfFunction(SMLoc ErrorLoc) {
+ TC.endOfFunction(ErrorLoc);
+
// Automatically output a .size directive, so it becomes optional for the
// user.
if (!LastFunctionLabel) return;
#define GET_SUBTARGET_FEATURE_NAME
#define GET_MATCHER_IMPLEMENTATION
#include "WebAssemblyGenAsmMatcher.inc"
+
+StringRef GetMnemonic(unsigned Opc) {
+ // FIXME: linear search!
+ for (auto &ME : MatchTable0) {
+ if (ME.Opcode == Opc) {
+ return ME.getMnemonic();
+ }
+ }
+ assert(false && "mnemonic not found");
+ return StringRef();
+}
--- /dev/null
+//==- WebAssemblyAsmTypeCheck.cpp - Assembler for WebAssembly -*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is part of the WebAssembly Assembler.
+///
+/// It contains code to translate a parsed .s file into MCInsts.
+///
+//===----------------------------------------------------------------------===//
+
+#include "AsmParser/WebAssemblyAsmTypeCheck.h"
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "MCTargetDesc/WebAssemblyTargetStreamer.h"
+#include "TargetInfo/WebAssemblyTargetInfo.h"
+#include "Utils/WebAssemblyTypeUtilities.h"
+#include "Utils/WebAssemblyUtilities.h"
+#include "WebAssembly.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
+#include "llvm/MC/MCSectionWasm.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCSymbol.h"
+#include "llvm/MC/MCSymbolWasm.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "wasm-asm-parser"
+
+extern StringRef GetMnemonic(unsigned Opc);
+
+namespace llvm {
+
+WebAssemblyAsmTypeCheck::WebAssemblyAsmTypeCheck(MCAsmParser &Parser,
+ const MCInstrInfo &MII, bool is64)
+ : Parser(Parser), MII(MII), is64(is64) {
+}
+
+void WebAssemblyAsmTypeCheck::funcDecl(const wasm::WasmSignature &Sig) {
+ LocalTypes.assign(Sig.Params.begin(), Sig.Params.end());
+ ReturnTypes.assign(Sig.Returns.begin(), Sig.Returns.end());
+}
+
+void WebAssemblyAsmTypeCheck::localDecl(const SmallVector<wasm::ValType, 4> &Locals) {
+ LocalTypes.insert(LocalTypes.end(), Locals.begin(), Locals.end());
+}
+
+void WebAssemblyAsmTypeCheck::dumpTypeStack(Twine Msg) {
+ LLVM_DEBUG({
+ std::string s;
+ for (auto VT : Stack) {
+ s += WebAssembly::typeToString(VT);
+ s += " ";
+ }
+ dbgs() << Msg << s << '\n';
+ });
+}
+
+bool WebAssemblyAsmTypeCheck::typeError(SMLoc ErrorLoc, const Twine &Msg) {
+ // Once you get one type error in a function, it will likely trigger more
+ // which are mostly not helpful.
+ if (TypeErrorThisFunction)
+ return true;
+ TypeErrorThisFunction = true;
+ dumpTypeStack("current stack: ");
+ return Parser.Error(ErrorLoc, Msg);
+}
+
+bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc,
+ Optional<wasm::ValType> EVT) {
+ if (Stack.empty()) {
+ return typeError(ErrorLoc,
+ EVT.hasValue()
+ ? StringRef("empty stack while popping ") +
+ WebAssembly::typeToString(EVT.getValue())
+ : StringRef(
+ "empty stack while popping value"));
+ }
+ auto PVT = Stack.back();
+ Stack.pop_back();
+ if (EVT.hasValue() && EVT.getValue() != PVT) {
+ return typeError(
+ ErrorLoc, StringRef("popped ") + WebAssembly::typeToString(PVT) +
+ ", expected " +
+ WebAssembly::typeToString(EVT.getValue()));
+ }
+ return false;
+}
+
+bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst,
+ wasm::ValType &Type) {
+ auto Local = Inst.getOperand(0).getImm();
+ if (static_cast<size_t>(Local) > LocalTypes.size())
+ return typeError(ErrorLoc, StringRef("no local type specified for index ") +
+ std::to_string(Local));
+ Type = LocalTypes[Local];
+ return false;
+}
+
+bool WebAssemblyAsmTypeCheck::checkEnd(SMLoc ErrorLoc) {
+ if (LastSig.Returns.size() > Stack.size())
+ return typeError(ErrorLoc, "end: insufficient values on the type stack");
+ for (size_t i = 0; i < LastSig.Returns.size(); i++) {
+ auto EVT = LastSig.Returns[i];
+ auto PVT = Stack[Stack.size() - LastSig.Returns.size() + i];
+ if (PVT != EVT)
+ return typeError(
+ ErrorLoc, StringRef("end got ") + WebAssembly::typeToString(PVT) +
+ ", expected " + WebAssembly::typeToString(EVT));
+ }
+ return false;
+}
+
+bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
+ const wasm::WasmSignature& Sig) {
+ for (auto VT : llvm::reverse(Sig.Params))
+ if (popType(ErrorLoc, VT)) return true;
+ Stack.insert(Stack.end(), Sig.Returns.begin(), Sig.Returns.end());
+ return false;
+}
+
+bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCInst &Inst,
+ const MCSymbolRefExpr *&SymRef) {
+ auto Op = Inst.getOperand(0);
+ if (!Op.isExpr())
+ return typeError(ErrorLoc, StringRef("expected expression operand"));
+ SymRef = dyn_cast<MCSymbolRefExpr>(Op.getExpr());
+ if (!SymRef)
+ return typeError(ErrorLoc, StringRef("expected symbol operand"));
+ return false;
+}
+
+bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst,
+ wasm::ValType &Type) {
+ const MCSymbolRefExpr *SymRef;
+ if (getSymRef(ErrorLoc, Inst, SymRef))
+ return true;
+ auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
+ switch (WasmSym->getType().getValueOr(wasm::WASM_SYMBOL_TYPE_DATA)) {
+ case wasm::WASM_SYMBOL_TYPE_GLOBAL:
+ Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type);
+ break;
+ case wasm::WASM_SYMBOL_TYPE_FUNCTION:
+ case wasm::WASM_SYMBOL_TYPE_DATA:
+ if (SymRef->getKind() == MCSymbolRefExpr::VK_GOT) {
+ Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32;
+ break;
+ }
+ // FALL-THROUGH:
+ default:
+ return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
+ " missing .globaltype");
+ }
+ return false;
+}
+
+void WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc) {
+ // Check the return types.
+ for (auto RVT : llvm::reverse(ReturnTypes)) {
+ popType(ErrorLoc, RVT);
+ }
+ if (!Stack.empty()) {
+ typeError(ErrorLoc,
+ std::to_string(Stack.size()) + " superfluous return values");
+ }
+ // Reset the type checker state.
+ Clear();
+}
+
+bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst) {
+ auto Opc = Inst.getOpcode();
+ auto Name = GetMnemonic(Opc);
+ dumpTypeStack("typechecking " + Name + ": ");
+ wasm::ValType Type;
+ if (Name == "local.get") {
+ if (getLocal(ErrorLoc, Inst, Type))
+ return true;
+ Stack.push_back(Type);
+ } else if (Name == "local.set") {
+ if (getLocal(ErrorLoc, Inst, Type))
+ return true;
+ if (popType(ErrorLoc, Type))
+ return true;
+ } else if (Name == "local.tee") {
+ if (getLocal(ErrorLoc, Inst, Type))
+ return true;
+ if (popType(ErrorLoc, Type))
+ return true;
+ Stack.push_back(Type);
+ } else if (Name == "global.get") {
+ if (getGlobal(ErrorLoc, Inst, Type))
+ return true;
+ Stack.push_back(Type);
+ } else if (Name == "global.set") {
+ if (getGlobal(ErrorLoc, Inst, Type))
+ return true;
+ if (popType(ErrorLoc, Type))
+ return true;
+ } else if (Name == "drop") {
+ if (popType(ErrorLoc, {}))
+ return true;
+ } else if (Name == "end_block" || Name == "end_loop" || Name == "end_if" ||
+ Name == "else") {
+ if (checkEnd(ErrorLoc))
+ return true;
+ } else if (Name == "call_indirect" || Name == "return_call_indirect") {
+ // Function value.
+ if (popType(ErrorLoc, wasm::ValType::I32)) return true;
+ if (checkSig(ErrorLoc, LastSig)) return true;
+ } else if (Name == "call" || Name == "return_call") {
+ const MCSymbolRefExpr *SymRef;
+ if (getSymRef(ErrorLoc, Inst, SymRef))
+ return true;
+ auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
+ auto Sig = WasmSym->getSignature();
+ if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_FUNCTION)
+ return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
+ " missing .functype");
+ if (checkSig(ErrorLoc, *Sig)) return true;
+ } else if (Name == "ref.null") {
+ auto VT = static_cast<wasm::ValType>(Inst.getOperand(0).getImm());
+ Stack.push_back(VT);
+ } else {
+ // The current instruction is a stack instruction which doesn't have
+ // explicit operands that indicate push/pop types, so we get those from
+ // the register version of the same instruction.
+ auto RegOpc = WebAssembly::getRegisterOpcode(Opc);
+ assert(RegOpc != -1 && "Failed to get register version of MC instruction");
+ const auto &II = MII.get(RegOpc);
+ // First pop all the uses off the stack and check them.
+ for (unsigned I = II.getNumOperands(); I > II.getNumDefs(); I--) {
+ const auto &Op = II.OpInfo[I - 1];
+ if (Op.OperandType == MCOI::OPERAND_REGISTER) {
+ auto VT = WebAssembly::regClassToValType(Op.RegClass);
+ if (popType(ErrorLoc, VT))
+ return true;
+ }
+ }
+ // Now push all the defs onto the stack.
+ for (unsigned I = 0; I < II.getNumDefs(); I++) {
+ const auto &Op = II.OpInfo[I];
+ assert(Op.OperandType == MCOI::OPERAND_REGISTER && "Register expected");
+ auto VT = WebAssembly::regClassToValType(Op.RegClass);
+ Stack.push_back(VT);
+ }
+ }
+ return false;
+}
+
+} // end namespace llvm
--- /dev/null
+//==- WebAssemblyAsmTypeCheck.h - Assembler for WebAssembly -*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file is part of the WebAssembly Assembler.
+///
+/// It contains code to translate a parsed .s file into MCInsts.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H
+#define LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H
+
+#include "llvm/MC/MCParser/MCAsmParser.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/BinaryFormat/Wasm.h"
+#include "llvm/MC/MCSymbol.h"
+
+namespace llvm {
+
+class WebAssemblyAsmTypeCheck final {
+ MCAsmParser &Parser;
+ const MCInstrInfo &MII;
+
+ SmallVector<wasm::ValType, 8> Stack;
+ SmallVector<wasm::ValType, 16> LocalTypes;
+ SmallVector<wasm::ValType, 4> ReturnTypes;
+ wasm::WasmSignature LastSig;
+ bool TypeErrorThisFunction = false;
+ bool is64;
+
+ void Clear() {
+ Stack.clear();
+ LocalTypes.clear();
+ ReturnTypes.clear();
+ TypeErrorThisFunction = false;
+ }
+
+ void dumpTypeStack(Twine Msg);
+ bool typeError(SMLoc ErrorLoc, const Twine &Msg);
+ bool popType(SMLoc ErrorLoc, Optional<wasm::ValType> EVT);
+ bool getLocal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type);
+ bool checkEnd(SMLoc ErrorLoc);
+ bool checkSig(SMLoc ErrorLoc, const wasm::WasmSignature &Sig);
+ bool getSymRef(SMLoc ErrorLoc, const MCInst &Inst,
+ const MCSymbolRefExpr *&SymRef);
+ bool getGlobal(SMLoc ErrorLoc, const MCInst &Inst, wasm::ValType &Type);
+
+public:
+ WebAssemblyAsmTypeCheck(MCAsmParser &Parser, const MCInstrInfo &MII, bool is64);
+
+ void funcDecl(const wasm::WasmSignature &Sig);
+ void localDecl(const SmallVector<wasm::ValType, 4> &Locals);
+ void setLastSig(const wasm::WasmSignature &Sig) { LastSig = Sig; }
+ void endOfFunction(SMLoc ErrorLoc);
+ bool typeCheck(SMLoc ErrorLoc, const MCInst &Inst);
+};
+
+} // end namespace llvm
+
+#endif LLVM_LIB_TARGET_WEBASSEMBLY_ASMPARSER_TYPECHECK_H
WebAssemblyLowerGlobalDtors.cpp
WebAssemblyMachineFunctionInfo.cpp
WebAssemblyMCInstLower.cpp
+ WebAssemblyMCLowerPrePass.cpp
WebAssemblyNullifyDebugValueLists.cpp
WebAssemblyOptimizeLiveIntervals.cpp
WebAssemblyOptimizeReturned.cpp
namespace WebAssembly {
int getStackOpcode(unsigned short Opcode);
+int getRegisterOpcode(unsigned short Opcode);
int getWasm64Opcode(unsigned short Opcode);
} // namespace WebAssembly
#include "WebAssemblyTypeUtilities.h"
#include "llvm/ADT/StringSwitch.h"
+// Get register classes enum.
+#define GET_REGINFO_ENUM
+#include "WebAssemblyGenRegisterInfo.inc"
+
using namespace llvm;
Optional<wasm::ValType> WebAssembly::parseType(StringRef Type) {
llvm_unreachable("unexpected type");
}
}
+
+wasm::ValType WebAssembly::regClassToValType(unsigned RC) {
+ switch (RC) {
+ case WebAssembly::I32RegClassID:
+ return wasm::ValType::I32;
+ case WebAssembly::I64RegClassID:
+ return wasm::ValType::I64;
+ case WebAssembly::F32RegClassID:
+ return wasm::ValType::F32;
+ case WebAssembly::F64RegClassID:
+ return wasm::ValType::F64;
+ case WebAssembly::V128RegClassID:
+ return wasm::ValType::V128;
+ case WebAssembly::FUNCREFRegClassID:
+ return wasm::ValType::FUNCREF;
+ case WebAssembly::EXTERNREFRegClassID:
+ return wasm::ValType::EXTERNREF;
+ default:
+ llvm_unreachable("unexpected type");
+ }
+}
// Convert a MVT into its corresponding wasm ValType.
wasm::ValType toValType(MVT Type);
+// Convert a register class to a wasm ValType.
+wasm::ValType regClassToValType(unsigned RC);
+
} // end namespace WebAssembly
} // end namespace llvm
FunctionPass *createWebAssemblyRegNumbering();
FunctionPass *createWebAssemblyDebugFixup();
FunctionPass *createWebAssemblyPeephole();
+FunctionPass *createWebAssemblyMCLowerPrePass();
// PassRegistry initialization declarations.
void initializeWebAssemblyAddMissingPrototypesPass(PassRegistry &);
void initializeWebAssemblyRegNumberingPass(PassRegistry &);
void initializeWebAssemblyDebugFixupPass(PassRegistry &);
void initializeWebAssemblyPeepholePass(PassRegistry &);
+void initializeWebAssemblyMCLowerPrePassPass(PassRegistry &);
namespace WebAssembly {
enum TargetIndex {
#include "WebAssemblyMCInstLower.h"
#include "WebAssemblyMachineFunctionInfo.h"
#include "WebAssemblyRegisterInfo.h"
+#include "WebAssemblyRuntimeLibcallSignatures.h"
#include "WebAssemblyTargetMachine.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
}
}
-void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
+MCSymbol *WebAssemblyAsmPrinter::getOrCreateWasmSymbol(StringRef Name) {
+ auto *WasmSym = cast<MCSymbolWasm>(GetExternalSymbolSymbol(Name));
+
+ // May be called multiple times, so early out.
+ if (WasmSym->getType().hasValue())
+ return WasmSym;
+
+ const WebAssemblySubtarget &Subtarget = getSubtarget();
+
+ // Except for certain known symbols, all symbols used by CodeGen are
+ // functions. It's OK to hardcode knowledge of specific symbols here; this
+ // method is precisely there for fetching the signatures of known
+ // Clang-provided symbols.
+ if (Name == "__stack_pointer" || Name == "__tls_base" ||
+ Name == "__memory_base" || Name == "__table_base" ||
+ Name == "__tls_size" || Name == "__tls_align") {
+ bool Mutable =
+ Name == "__stack_pointer" || Name == "__tls_base";
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
+ WasmSym->setGlobalType(wasm::WasmGlobalType{
+ uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
+ : wasm::WASM_TYPE_I32),
+ Mutable});
+ return WasmSym;
+ }
+
+ SmallVector<wasm::ValType, 4> Returns;
+ SmallVector<wasm::ValType, 4> Params;
+ if (Name == "__cpp_exception") {
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG);
+ // We can't confirm its signature index for now because there can be
+ // imported exceptions. Set it to be 0 for now.
+ WasmSym->setTagType(
+ {wasm::WASM_TAG_ATTRIBUTE_EXCEPTION, /* SigIndex */ 0});
+ // We may have multiple C++ compilation units to be linked together, each of
+ // which defines the exception symbol. To resolve them, we declare them as
+ // weak.
+ WasmSym->setWeak(true);
+ WasmSym->setExternal(true);
+
+ // All C++ exceptions are assumed to have a single i32 (for wasm32) or i64
+ // (for wasm64) param type and void return type. The reaon is, all C++
+ // exception values are pointers, and to share the type section with
+ // functions, exceptions are assumed to have void return type.
+ Params.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64
+ : wasm::ValType::I32);
+ } else { // Function symbols
+ WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
+ getLibcallSignature(Subtarget, Name, Returns, Params);
+ }
+ auto Signature = std::make_unique<wasm::WasmSignature>(std::move(Returns),
+ std::move(Params));
+ WasmSym->setSignature(Signature.get());
+ addSignature(std::move(Signature));
+
+ return WasmSym;
+}
+
+void WebAssemblyAsmPrinter::emitExternalDecls(const Module &M) {
+ if (signaturesEmitted)
+ return;
+ signaturesEmitted = true;
+
+ // Normally symbols for globals get discovered as the MI gets lowered,
+ // but we need to know about them ahead of time.
+ MachineModuleInfoWasm &MMIW = MMI->getObjFileInfo<MachineModuleInfoWasm>();
+ for (const auto &Name : MMIW.MachineSymbolsUsed) {
+ getOrCreateWasmSymbol(Name.getKey());
+ }
+
for (auto &It : OutContext.getSymbols()) {
// Emit .globaltype, .tagtype, or .tabletype declarations.
auto Sym = cast<MCSymbolWasm>(It.getValue());
}
DenseSet<MCSymbol *> InvokeSymbols;
- bool HasAddressTakenFunction = false;
for (const auto &F : M) {
if (F.isIntrinsic())
continue;
- if (F.hasAddressTaken())
- HasAddressTakenFunction = true;
-
// Emit function type info for all undefined functions
if (F.isDeclarationForLinker()) {
SmallVector<MVT, 4> Results;
getTargetStreamer()->emitExportName(Sym, Name);
}
}
+}
+
+void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
+ emitExternalDecls(M);
// When a function's address is taken, a TABLE_INDEX relocation is emitted
// against the function symbol at the use site. However the relocation
// define a new kind of reloc against both the function and the table, so
// that the linker can see that the function symbol keeps the table alive,
// but for now manually mark the table as live.
- if (HasAddressTakenFunction) {
- MCSymbolWasm *FunctionTable =
- WebAssembly::getOrCreateFunctionTableSymbol(OutContext, Subtarget);
- OutStreamer->emitSymbolAttribute(FunctionTable, MCSA_NoDeadStrip);
+ for (const auto &F : M) {
+ if (!F.isIntrinsic() && F.hasAddressTaken()) {
+ MCSymbolWasm *FunctionTable =
+ WebAssembly::getOrCreateFunctionTableSymbol(OutContext, Subtarget);
+ OutStreamer->emitSymbolAttribute(FunctionTable, MCSA_NoDeadStrip);
+ break;
+ }
}
for (const auto &G : M.globals()) {
// Nothing to do; jump tables are incorporated into the instruction stream.
}
+void WebAssemblyAsmPrinter::emitLinkage(const GlobalValue *GV, MCSymbol *Sym)
+ const {
+ AsmPrinter::emitLinkage(GV, Sym);
+ // This gets called before the function label and type are emitted.
+ // We use it to emit signatures of external functions.
+ // FIXME casts!
+ const_cast<WebAssemblyAsmPrinter *>(this)
+ ->emitExternalDecls(*MMI->getModule());
+}
+
+
void WebAssemblyAsmPrinter::emitFunctionBodyStart() {
const Function &F = MF->getFunction();
SmallVector<MVT, 1> ResultVTs;
// TODO: Do the uniquing of Signatures here instead of ObjectFileWriter?
std::vector<std::unique_ptr<wasm::WasmSignature>> Signatures;
std::vector<std::unique_ptr<std::string>> Names;
+ bool signaturesEmitted = false;
StringRef storeName(StringRef Name) {
std::unique_ptr<std::string> N = std::make_unique<std::string>(Name);
void emitGlobalVariable(const GlobalVariable *GV) override;
void emitJumpTableInfo() override;
void emitConstantPool() override;
+ void emitLinkage(const GlobalValue *, MCSymbol *) const override;
void emitFunctionBodyStart() override;
void emitInstruction(const MachineInstr *MI) override;
bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
MCSymbolWasm *getMCSymbolForFunction(const Function *F, bool EnableEmEH,
wasm::WasmSignature *Sig,
bool &InvokeDetected);
+ MCSymbol *getOrCreateWasmSymbol(StringRef Name);
+ void emitExternalDecls(const Module &M);
};
} // end namespace llvm
// We instantiate 2 of these for every actual instruction (register based
// and stack based), see below.
class WebAssemblyInst<bits<32> inst, string asmstr, string stack, string is64>
- : StackRel, Wasm64Rel, Instruction {
+ : StackRel, RegisterRel, Wasm64Rel, Instruction {
bits<32> Inst = inst; // Instruction encoding.
string StackBased = stack;
string BaseName = NAME;
}
//===----------------------------------------------------------------------===//
+// WebAssembly Stack to Register instruction mapping
+//===----------------------------------------------------------------------===//
+
+class RegisterRel;
+def getRegisterOpcode : InstrMapping {
+ let FilterClass = "RegisterRel";
+ let RowFields = ["BaseName"];
+ let ColFields = ["StackBased"];
+ let KeyCol = ["true"];
+ let ValueCols = [["false"]];
+}
+
+//===----------------------------------------------------------------------===//
// WebAssembly 32 to 64-bit instruction mapping
//===----------------------------------------------------------------------===//
multiclass TABLE<WebAssemblyRegClass rt> {
- defm TABLE_GET_#rt : I<(outs rt:$res), (ins table32_op:$table),
+ defm TABLE_GET_#rt : I<(outs rt:$res), (ins table32_op:$table, I32:$i),
(outs), (ins table32_op:$table),
[],
- "table.get\t$res, $table",
+ "table.get\t$res, $table, $i",
"table.get\t$table",
0x25>;
#include "Utils/WebAssemblyUtilities.h"
#include "WebAssemblyAsmPrinter.h"
#include "WebAssemblyMachineFunctionInfo.h"
-#include "WebAssemblyRuntimeLibcallSignatures.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/IR/Constants.h"
MCSymbol *WebAssemblyMCInstLower::GetExternalSymbolSymbol(
const MachineOperand &MO) const {
- const char *Name = MO.getSymbolName();
- auto *WasmSym = cast<MCSymbolWasm>(Printer.GetExternalSymbolSymbol(Name));
- const WebAssemblySubtarget &Subtarget = Printer.getSubtarget();
-
- // Except for certain known symbols, all symbols used by CodeGen are
- // functions. It's OK to hardcode knowledge of specific symbols here; this
- // method is precisely there for fetching the signatures of known
- // Clang-provided symbols.
- if (strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0 ||
- strcmp(Name, "__memory_base") == 0 || strcmp(Name, "__table_base") == 0 ||
- strcmp(Name, "__tls_size") == 0 || strcmp(Name, "__tls_align") == 0) {
- bool Mutable =
- strcmp(Name, "__stack_pointer") == 0 || strcmp(Name, "__tls_base") == 0;
- WasmSym->setType(wasm::WASM_SYMBOL_TYPE_GLOBAL);
- WasmSym->setGlobalType(wasm::WasmGlobalType{
- uint8_t(Subtarget.hasAddr64() ? wasm::WASM_TYPE_I64
- : wasm::WASM_TYPE_I32),
- Mutable});
- return WasmSym;
- }
-
- SmallVector<wasm::ValType, 4> Returns;
- SmallVector<wasm::ValType, 4> Params;
- if (strcmp(Name, "__cpp_exception") == 0) {
- WasmSym->setType(wasm::WASM_SYMBOL_TYPE_TAG);
- // We can't confirm its signature index for now because there can be
- // imported exceptions. Set it to be 0 for now.
- WasmSym->setTagType({wasm::WASM_TAG_ATTRIBUTE_EXCEPTION, /* SigIndex */ 0});
- // We may have multiple C++ compilation units to be linked together, each of
- // which defines the exception symbol. To resolve them, we declare them as
- // weak.
- WasmSym->setWeak(true);
- WasmSym->setExternal(true);
-
- // All C++ exceptions are assumed to have a single i32 (for wasm32) or i64
- // (for wasm64) param type and void return type. The reaon is, all C++
- // exception values are pointers, and to share the type section with
- // functions, exceptions are assumed to have void return type.
- Params.push_back(Subtarget.hasAddr64() ? wasm::ValType::I64
- : wasm::ValType::I32);
- } else { // Function symbols
- WasmSym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION);
- getLibcallSignature(Subtarget, Name, Returns, Params);
- }
- auto Signature =
- std::make_unique<wasm::WasmSignature>(std::move(Returns), std::move(Params));
- WasmSym->setSignature(Signature.get());
- Printer.addSignature(std::move(Signature));
-
- return WasmSym;
+ return Printer.getOrCreateWasmSymbol(MO.getSymbolName());
}
MCOperand WebAssemblyMCInstLower::lowerSymbolOperand(const MachineOperand &MO,
--- /dev/null
+//===-- WebAssemblyMCLowerPrePass.cpp - Prepare for MC lower --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Some information in MC lowering / asm printing gets generated as
+/// instructions get emitted, but may be necessary at the start, such as for
+/// .globaltype declarations. This pass collects this information.
+///
+//===----------------------------------------------------------------------===//
+
+#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
+#include "Utils/WebAssemblyUtilities.h"
+#include "WebAssembly.h"
+#include "WebAssemblyMachineFunctionInfo.h"
+#include "WebAssemblySubtarget.h"
+#include "llvm/ADT/SCCIterator.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineLoopInfo.h"
+#include "llvm/CodeGen/MachineModuleInfoImpls.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/Passes.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
+
+#define DEBUG_TYPE "wasm-mclower-prepass"
+
+namespace {
+class WebAssemblyMCLowerPrePass final : public MachineFunctionPass {
+ StringRef getPassName() const override {
+ return "WebAssembly MC Lower Pre Pass";
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesCFG();
+ MachineFunctionPass::getAnalysisUsage(AU);
+ }
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+public:
+ static char ID; // Pass identification, replacement for typeid
+ WebAssemblyMCLowerPrePass() : MachineFunctionPass(ID) {}
+};
+} // end anonymous namespace
+
+char WebAssemblyMCLowerPrePass::ID = 0;
+INITIALIZE_PASS(
+ WebAssemblyMCLowerPrePass, DEBUG_TYPE,
+ "Collects information ahead of time for MC lowering",
+ false, false)
+
+FunctionPass *llvm::createWebAssemblyMCLowerPrePass() {
+ return new WebAssemblyMCLowerPrePass();
+}
+
+bool WebAssemblyMCLowerPrePass::runOnMachineFunction(MachineFunction &MF) {
+ LLVM_DEBUG(dbgs() << "********** MC Lower Pre Pass **********\n"
+ "********** Function: "
+ << MF.getName() << '\n');
+
+ MachineModuleInfo &MMI = MF.getMMI();
+ MachineModuleInfoWasm &MMIW = MMI.getObjFileInfo<MachineModuleInfoWasm>();
+
+ for (MachineBasicBlock &MBB : MF) {
+ for (auto &MI : MBB) {
+ // FIXME: what should all be filtered out beyond these?
+ if (MI.isDebugInstr() || MI.isInlineAsm())
+ continue;
+ for (MachineOperand &MO : MI.uses()) {
+ if (MO.isSymbol()) {
+ MMIW.MachineSymbolsUsed.insert(MO.getSymbolName());
+ }
+ }
+ }
+ }
+
+ return true;
+}
// TODO: If the RTLIB::Libcall-taking flavor of GetSignature remains unsed
// other than here, just roll its logic into this version.
void llvm::getLibcallSignature(const WebAssemblySubtarget &Subtarget,
- const char *Name,
+ StringRef Name,
SmallVectorImpl<wasm::ValType> &Rets,
SmallVectorImpl<wasm::ValType> &Params) {
auto &Map = LibcallNameMap->Map;
auto Val = Map.find(Name);
#ifndef NDEBUG
if (Val == Map.end()) {
- auto message = std::string("unexpected runtime library name: ") + Name;
+ auto message = std::string("unexpected runtime library name: ") +
+ std::string(Name);
llvm_unreachable(message.c_str());
}
#endif
SmallVectorImpl<wasm::ValType> &Params);
extern void getLibcallSignature(const WebAssemblySubtarget &Subtarget,
- const char *Name,
+ StringRef Name,
SmallVectorImpl<wasm::ValType> &Rets,
SmallVectorImpl<wasm::ValType> &Params);
initializeWebAssemblyRegNumberingPass(PR);
initializeWebAssemblyDebugFixupPass(PR);
initializeWebAssemblyPeepholePass(PR);
+ initializeWebAssemblyMCLowerPrePassPass(PR);
}
//===----------------------------------------------------------------------===//
// Fix debug_values whose defs have been stackified.
if (!WasmDisableExplicitLocals)
addPass(createWebAssemblyDebugFixup());
+
+ // Collect information to prepare for MC lowering / asm printing.
+ addPass(createWebAssemblyMCLowerPrePass());
}
yaml::MachineFunctionInfo *
@_ZTIi = external dso_local constant i8*
+; CHECK: .tagtype __cpp_exception i32
+
; CHECK-LABEL: test_throw:
; CHECK: throw __cpp_exception, $0
; CHECK-NOT: unreachable
attributes #1 = { noreturn }
; CHECK: __cpp_exception:
-; CHECK: .tagtype __cpp_exception i32
; CHECK: .globaltype f64_global, f64
; CHECK-LABEL: f64_global:
+; FIXME: are we still expecting these to be emitted?
+
; CHECK-NOT: .global i32_external_used
-; CHECK: .globaltype i32_external_used, i32
+; CHECK-NOT: .globaltype i32_external_used, i32
; CHECK-NOT: i32_external_used:
; CHECK-NOT: .global i32_external_unused
-; CHECK: .globaltype i32_external_unused, i32
+; CHECK-NOT: .globaltype i32_external_unused, i32
; CHECK-NOT: i32_external_unused:
ret i128 %r
}
+; CHECK: .functype bar (i32, i64, i64) -> ()
+
; CHECK-LABEL: foo:
; CHECK-NEXT: .functype foo (i32, i64, i64) -> ()
-; CHECK: .functype bar (i32, i64, i64) -> ()
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32"
+; CHECK: .functype extern_fd (f64) -> (f32)
+; CHECK: .functype extern_vj (i64) -> ()
+; CHECK: .functype extern_v () -> ()
+; CHECK: .functype extern_ijidf (i64, i32, f64, f32) -> (i32)
+; CHECK: .functype extern_struct (i32) -> ()
+; CHECK: .functype extern_sret (i32) -> ()
+; CHECK: .functype extern_i128ret (i32, i64) -> ()
+
%struct.big = type { float, double, i32 }
; Function Attrs: nounwind
attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "frame-pointer"="none" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
-; CHECK: .functype extern_fd (f64) -> (f32)
-; CHECK: .functype extern_vj (i64) -> ()
-; CHECK: .functype extern_v () -> ()
-; CHECK: .functype extern_ijidf (i64, i32, f64, f32) -> (i32)
-; CHECK: .functype extern_struct (i32) -> ()
-; CHECK: .functype extern_sret (i32) -> ()
-; CHECK: .functype extern_i128ret (i32, i64) -> ()
@external_global = external global i32
@external_global_array = external global [10 x i32]
+; PIC: .globaltype __memory_base, [[PTR]]
+
declare i32 @foo();
; For hidden symbols PIC code needs to offset all loads and stores
store i32 %n, i32* %1
ret void
}
-
-; PIC: .globaltype __memory_base, [[PTR]]
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-unknown"
+; EH: .functype invoke_vi (i32, i32) -> ()
+; EH: .import_module invoke_vi, env
+; EH: .import_name invoke_vi, invoke_vi
+; EH-NOT: .functype __invoke_void_i32
+; EH-NOT: .import_module __invoke_void_i32
+; EH-NOT: .import_name __invoke_void_i32
+
+; SJLJ: .functype emscripten_longjmp (i32, i32) -> ()
+; SJLJ: .import_module emscripten_longjmp, env
+; SJLJ: .import_name emscripten_longjmp, emscripten_longjmp
+; SJLJ-NOT: .functype emscripten_longjmp_jmpbuf
+; SJLJ-NOT: .import_module emscripten_longjmp_jmpbuf
+; SJLJ-NOT: .import_name emscripten_longjmp_jmpbuf
+
%struct.__jmp_buf_tag = type { [6 x i32], i32, [32 x i32] }
define void @exception() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
attributes #1 = { noreturn }
attributes #2 = { nounwind }
-; EH: .functype invoke_vi (i32, i32) -> ()
-; EH: .import_module invoke_vi, env
-; EH: .import_name invoke_vi, invoke_vi
-; EH-NOT: .functype __invoke_void_i32
-; EH-NOT: .import_module __invoke_void_i32
-; EH-NOT: .import_name __invoke_void_i32
-
-; SJLJ: .functype emscripten_longjmp (i32, i32) -> ()
-; SJLJ: .import_module emscripten_longjmp, env
-; SJLJ: .import_name emscripten_longjmp, emscripten_longjmp
-; SJLJ-NOT: .functype emscripten_longjmp_jmpbuf
-; SJLJ-NOT: .import_module emscripten_longjmp_jmpbuf
-; SJLJ-NOT: .import_name emscripten_longjmp_jmpbuf
-
; WASM-EH-EM-EH: LLVM ERROR: -exception-model=wasm not allowed with -enable-emscripten-cxx-exceptions
{ i32, void ()*, i8* } { i32 65535, void ()* @after_the_null, i8* null }
]
+; CHECK-LABEL: .functype __cxa_atexit (i32, i32, i32) -> (i32){{$}}
+
; CHECK-LABEL: .Lcall_dtors.0:
; CHECK-NEXT: .functype .Lcall_dtors.0 (i32) -> (){{$}}
; CHECK-NEXT: call orig_dtor0{{$}}
; CHECK-LABEL: .weak __dso_handle
-; CHECK-LABEL: .functype __cxa_atexit (i32, i32, i32) -> (i32){{$}}
-
; We shouldn't make use of a .fini_array section.
; FINI-NOT: fini_array
declare void @ext_func(i64* %ptr)
declare void @ext_func_i32(i32* %ptr)
+; CHECK: .globaltype __stack_pointer, i[[PTR]]{{$}}
+
; CHECK-LABEL: alloca32:
; Check that there is an extra local for the stack pointer.
; CHECK: .local i[[PTR]]{{$}}
ret i8 %5
}
-; CHECK: .globaltype __stack_pointer, i[[PTR]]{{$}}
-
; TODO: test over-aligned alloca
.section .text,"",@
main:
.functype main () -> ()
+ i32.const 0
i32.const sym_a
i32.store sym_b
end_function
# CHECK-LABEL: <main>:
# CHECK-EMPTY:
-# CHECK-NEXT: 3: 41 84 80 80 80 00 i32.const 4
-# CHECK-NEXT: 00000004: R_WASM_MEMORY_ADDR_SLEB sym_a+0
-# CHECK-NEXT: 9: 36 02 88 80 80 80 00 i32.store 8
-# CHECK-NEXT: 0000000b: R_WASM_MEMORY_ADDR_LEB sym_b+0
-# CHECK-NEXT: 10: 0b end
+# CHECK-NEXT: 3: 41 00 i32.const 0
+# CHECK-NEXT: 5: 41 84 80 80 80 00 i32.const 4
+# CHECK-NEXT: 00000006: R_WASM_MEMORY_ADDR_SLEB sym_a+0
+# CHECK-NEXT: b: 36 02 88 80 80 80 00 i32.store 8
+# CHECK-NEXT: 0000000d: R_WASM_MEMORY_ADDR_LEB sym_b+0
+# CHECK-NEXT: 12: 0b end
-# RUN: llvm-mc -triple=wasm32-unknown-unknown -mattr=+exception-handling < %s | FileCheck %s
+# RUN: llvm-mc -no-type-check -triple=wasm32-unknown-unknown -mattr=+exception-handling < %s | FileCheck %s
# Tests if block/loop/try/catch/end/branch/rethrow instructions are correctly
# printed with their annotations.
; ASM: .text
; ASM: .file "assembler-binary.ll"
; ASM: .globl foo
+; ASM: .functype bar () -> ()
; ASM: foo:
; ASM-NEXT: .functype foo (i32) -> ()
; ASM-NEXT: call bar
; ASM-NEXT: end_function
-; ASM: .functype bar () -> ()
; CHECK: --- !WASM
-# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+atomics < %s | FileCheck %s
+# RUN: llvm-mc -no-type-check -show-encoding -triple=wasm32-unknown-unknown -mattr=+atomics < %s | FileCheck %s
main:
.functype main () -> ()
.text
.section .text.main,"",@
main:
- .functype main () -> (i32)
+ .functype main () -> (i32, i32, i32, i32, i32)
// Expressions involving symbols within the same sections can be evaluated
// prior to writing the object file.
// CHECK-NOT: foo
# Check that it converts to .o without errors, but don't check any output:
# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -mattr=+reference-types,+atomics,+simd128,+nontrapping-fptoint,+exception-handling -o %t.o < %s
+.functype something1 () -> ()
+.functype something2 (i64) -> (i32, f64)
+.globaltype __stack_pointer, i32
empty_func:
.functype empty_func () -> ()
local.get 2
local.set 2
# Immediates:
- i32.const -1
- f64.const 0x1.999999999999ap1
f32.const -1.0
+ drop
f32.const -infinity
- f32.const nan
+ drop
v128.const 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ drop
v128.const 0, 1, 2, 3, 4, 5, 6, 7
- # Indirect addressing:
+ drop
local.get 0
+ f64.const 0x1.999999999999ap1
+ # Indirect addressing:
f64.store 1234:p2align=4
+ i32.const -1
+ f64.const nan
f64.store 1234 # Natural alignment (3)
# Loops, conditionals, binary ops, calls etc:
- block i32
+ block f32
+ f32.const 2.0
i32.const 1
local.get 0
i32.ge_s
br_if 0 # 0: down to label0
.LBB0_1:
- loop i32 # label1:
+ loop void # label1:
call something1
i64.const 1234
call something2
i32.const 0
call_indirect (i32, f64) -> ()
i32.const 1
+ i32.const 2
i32.add
local.tee 0
local.get 0
.LBB0_2:
end_loop
end_block # label0:
- local.get 4
- local.get 5
+ drop
+ block i32
+ block void
+ block void
block void
- block i64
- block f32
- block f64
block () -> (i32, i32)
i32.const 1
i32.const 2
end_block
drop
- drop
br_table {0, 1, 2} # 2 entries, default
end_block # first entry jumps here.
i32.const 1
end_if
else
end_if
+ drop
+ local.get 4
+ local.get 5
f32x4.add
+ drop
# Test correct parsing of instructions with / and : in them:
# TODO: enable once instruction has been added.
#i32x4.trunc_sat_f32x4_s
+ f32.const 1.0
i32.trunc_f32_s
try
i32.atomic.load 0
+ i32.const 0
memory.atomic.notify 0
+ drop
.LBB0_3:
catch __cpp_exception
local.set 0
.LBB0_4:
#i32.trunc_sat_f32_s
global.get __stack_pointer
+ global.set __stack_pointer
end_function
.section .rodata..L.str,"",@
.int32 test0
.ident "clang version 9.0.0 (trunk 364502) (llvm/trunk 364571)"
- .globaltype __stack_pointer, i32
.tabletype empty_eref_table, externref
empty_eref_table:
# CHECK: .text
+# CHECK: .globaltype __stack_pointer, i32
+
# CHECK-LABEL: empty_func:
# CHECK-NEXT: .functype empty_func () -> ()
# CHECK-NEXT: end_function
# CHECK-NEXT: .local f32, f64
# CHECK-NEXT: local.get 2
# CHECK-NEXT: local.set 2
-# CHECK-NEXT: i32.const -1
-# CHECK-NEXT: f64.const 0x1.999999999999ap1
# CHECK-NEXT: f32.const -0x1p0
+# CHECK-NEXT: drop
# CHECK-NEXT: f32.const -infinity
-# CHECK-NEXT: f32.const nan
+# CHECK-NEXT: drop
# CHECK-NEXT: v128.const 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+# CHECK-NEXT: drop
# CHECK-NEXT: v128.const 0, 1, 2, 3, 4, 5, 6, 7
+# CHECK-NEXT: drop
# CHECK-NEXT: local.get 0
+# CHECK-NEXT: f64.const 0x1.999999999999ap1
# CHECK-NEXT: f64.store 1234:p2align=4
+# CHECK-NEXT: i32.const -1
+# CHECK-NEXT: f64.const nan
# CHECK-NEXT: f64.store 1234
-# CHECK-NEXT: block i32
+# CHECK-NEXT: block f32
+# CHECK-NEXT: f32.const 0x1p1
# CHECK-NEXT: i32.const 1
# CHECK-NEXT: local.get 0
# CHECK-NEXT: i32.ge_s
# CHECK-NEXT: br_if 0 # 0: down to label0
# CHECK-NEXT: .LBB0_1:
-# CHECK-NEXT: loop i32 # label1:
+# CHECK-NEXT: loop # label1:
# CHECK-NEXT: call something1
# CHECK-NEXT: i64.const 1234
# CHECK-NEXT: call something2
# CHECK-NEXT: i32.const 0
# CHECK-NEXT: call_indirect __indirect_function_table, (i32, f64) -> ()
# CHECK-NEXT: i32.const 1
+# CHECK-NEXT: i32.const 2
# CHECK-NEXT: i32.add
# CHECK-NEXT: local.tee 0
# CHECK-NEXT: local.get 0
# CHECK-NEXT: .LBB0_2:
# CHECK-NEXT: end_loop
# CHECK-NEXT: end_block # label0:
-# CHECK-NEXT: local.get 4
-# CHECK-NEXT: local.get 5
+# CHECK-NEXT: drop
+# CHECK-NEXT: block i32
+# CHECK-NEXT: block
+# CHECK-NEXT: block
# CHECK-NEXT: block
-# CHECK-NEXT: block i64
-# CHECK-NEXT: block f32
-# CHECK-NEXT: block f64
# CHECK-NEXT: block () -> (i32, i32)
# CHECK-NEXT: i32.const 1
# CHECK-NEXT: i32.const 2
# CHECK-NEXT: end_block
# CHECK-NEXT: drop
-# CHECK-NEXT: drop
# CHECK-NEXT: br_table {0, 1, 2} # 1: down to label4
# CHECK-NEXT: # 2: down to label3
# CHECK-NEXT: end_block # label5:
# CHECK-NEXT: end_if
# CHECK-NEXT: else
# CHECK-NEXT: end_if
+# CHECK-NEXT: drop
+# CHECK-NEXT: local.get 4
+# CHECK-NEXT: local.get 5
# CHECK-NEXT: f32x4.add
+# CHECK-NEXT: drop
+# CHECK-NEXT: f32.const 0x1p0
# CHECK-NEXT: i32.trunc_f32_s
# CHECK-NEXT: try
# CHECK-NEXT: i32.atomic.load 0
+# CHECK-NEXT: i32.const 0
# CHECK-NEXT: memory.atomic.notify 0
+# CHECK-NEXT: drop
# CHECK-NEXT: .LBB0_3:
# CHECK-NEXT: catch __cpp_exception
# CHECK-NEXT: local.set 0
# CHECK-NEXT: throw 0
# CHECK-NEXT: .LBB0_4:
# CHECK-NEXT: global.get __stack_pointer
+# CHECK-NEXT: global.set __stack_pointer
# CHECK-NEXT: end_function
# CHECK: .section .rodata..L.str,"",@
# CHECK-NEXT: .p2align 2
# CHECK-NEXT: .int32 test0
-# CHECK: .globaltype __stack_pointer, i32
-
# CHECK: .tabletype empty_eref_table, externref
# CHECK-NEXT: empty_eref_table:
-# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+bulk-memory < %s | FileCheck %s
-# RUN: llvm-mc -show-encoding -triple=wasm64-unknown-unknown -mattr=+bulk-memory < %s | FileCheck %s
+# RUN: llvm-mc -show-encoding -no-type-check -triple=wasm32-unknown-unknown -mattr=+bulk-memory < %s | FileCheck %s
+# RUN: llvm-mc -show-encoding -no-type-check -triple=wasm64-unknown-unknown -mattr=+bulk-memory < %s | FileCheck %s
main:
.functype main () -> ()
# RUN: llvm-mc -triple=wasm32-unknown-unknown < %s | FileCheck %s
test0:
- .functype test0 () -> (i32)
+ .functype test0 () -> (i32, i32)
i32.const a
i32.const b
end_function
end_function
write_global:
- .functype write_global (i32) -> ()
+ .functype write_global (i32, i64, f32, f64) -> ()
local.get 0
global.set foo_global
+ local.get 1
global.set global2
+ local.get 2
global.set global3
+ local.get 3
global.set global4
end_function
# RUN: llvm-mc -triple=wasm32 < %s | FileCheck %s -check-prefix=CHECK-ASM
# RUN: llvm-mc -triple=wasm32 -filetype=obj -o - < %s | obj2yaml | FileCheck %s
+.functype foo () -> ()
+.functype plain () -> ()
+
test:
.functype test () -> ()
call foo
call plain
end_function
- .functype foo () -> ()
- .functype plain () -> ()
.import_module foo, bar
.import_name foo, qux
.type main,@function
main:
.functype main (i32, i32) -> (i32)
+ local.get 0
end_function
.Lfunc_end0:
.size main, .Lfunc_end0-main
# RUN: llvm-objdump --triple=wasm32-unknown-unknown -d %t.o | FileCheck %s
test0:
- .functype test0 (i32, i64) -> (i32)
+ .functype test0 (i32, i64) -> (f32)
.local f32, f64, v128, v128
local.get 2
end_function
test1:
- .functype test1 (i32, i64) -> (i32)
+ .functype test1 (i32, i64) -> (i64)
.local i32, i64, funcref
local.get 3
end_function
# CHECK-NEXT: .functype ref_sig_test_funcref (funcref) -> (funcref)
ref_sig_test_funcref:
.functype ref_sig_test_funcref (funcref) -> (funcref)
+ local.get 0
end_function
# CHECK-LABEL: ref_sig_test_externref:
# CHECK-NEXT: .functype ref_sig_test_externref (externref) -> (externref)
ref_sig_test_externref:
.functype ref_sig_test_externref (externref) -> (externref)
+ local.get 0
end_function
# CHECK-LABEL: ref_select_test:
# CHECK: block funcref
# CHECK: block externref
ref_block_test:
- .functype ref_block_test () -> ()
+ .functype ref_block_test () -> (externref, funcref)
block funcref
block externref
+ ref.null extern
end_block
+ ref.null func
end_block
end_function
# against the corrsponding function or data symbol and that the corresponding
# data symbols are imported as a wasm globals.
+.functype default_func () -> (i32)
+
+.globaltype __memory_base, i32
+.globaltype __table_base, i32
+
load_default_data:
.functype load_default_data () -> (i32)
global.get default_data@GOT
#.hidden hidden_func
#.hidden hidden_data
.size default_data, 4
-.functype default_func () -> (i32)
# CHECK: --- !WASM
# CHECK-NEXT: FileHeader:
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: SigIndex: 0
# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: __memory_base
+# CHECK-NEXT: Kind: GLOBAL
+# CHECK-NEXT: GlobalType: I32
+# CHECK-NEXT: GlobalMutable: true
+# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: __table_base
+# CHECK-NEXT: Kind: GLOBAL
+# CHECK-NEXT: GlobalType: I32
+# CHECK-NEXT: GlobalMutable: true
+# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __indirect_function_table
# CHECK-NEXT: Kind: TABLE
# CHECK-NEXT: Table:
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# CHECK-NEXT: Index: 3
# CHECK-NEXT: Offset: 0x10
-# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
+# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# CHECK-NEXT: Index: 5
# CHECK-NEXT: Offset: 0x1C
# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_REL_SLEB
# CHECK-NEXT: Index: 6
# CHECK-NEXT: Offset: 0x22
-# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
+# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# CHECK-NEXT: Index: 8
# CHECK-NEXT: Offset: 0x2C
# CHECK-NEXT: - Type: R_WASM_TABLE_INDEX_REL_SLEB
# CHECK-NEXT: Functions:
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: Locals: []
-# CHECK-NEXT: Body: 2380808080002802000B
+# CHECK-NEXT: Body: 2382808080002802000B
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Locals: []
-# CHECK-NEXT: Body: 2381808080002802000B
+# CHECK-NEXT: Body: 2383808080002802000B
# CHECK-NEXT: - Index: 3
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 2380808080004180808080006A0B
# CHECK-NEXT: - Index: 4
# CHECK-NEXT: Locals: []
-# CHECK-NEXT: Body: 2380808080004180808080006A0B
+# CHECK-NEXT: Body: 2381808080004180808080006A0B
# CHECK-NEXT: - Index: 5
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 41000B
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Function: 3
# CHECK-NEXT: - Index: 5
-# CHECK-NEXT: Kind: DATA
+# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: Name: __memory_base
# CHECK-NEXT: Flags: [ UNDEFINED ]
+# CHECK-NEXT: Global: 0
# CHECK-NEXT: - Index: 6
# CHECK-NEXT: Kind: DATA
# CHECK-NEXT: Name: .L.hidden_data
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Function: 4
# CHECK-NEXT: - Index: 8
-# CHECK-NEXT: Kind: DATA
+# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: Name: __table_base
# CHECK-NEXT: Flags: [ UNDEFINED ]
+# CHECK-NEXT: Global: 1
# CHECK-NEXT: - Index: 9
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Name: hidden_func
# against the corrsponding function or data symbol and that the corresponding
# data symbols are imported as a wasm globals.
+.functype default_func () -> (i32)
+
+.globaltype __memory_base, i64
+.globaltype __table_base, i64
+
load_default_data:
.functype load_default_data () -> (i32)
global.get default_data@GOT
#.hidden hidden_func
#.hidden hidden_data
.size default_data, 4
-.functype default_func () -> (i32)
# CHECK: --- !WASM
# CHECK-NEXT: FileHeader:
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: SigIndex: 0
# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: __memory_base
+# CHECK-NEXT: Kind: GLOBAL
+# CHECK-NEXT: GlobalType: I64
+# CHECK-NEXT: GlobalMutable: true
+# CHECK-NEXT: - Module: env
+# CHECK-NEXT: Field: __table_base
+# CHECK-NEXT: Kind: GLOBAL
+# CHECK-NEXT: GlobalType: I64
+# CHECK-NEXT: GlobalMutable: true
+# CHECK-NEXT: - Module: env
# CHECK-NEXT: Field: __indirect_function_table
# CHECK-NEXT: Kind: TABLE
# CHECK-NEXT: Table:
# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# CHECK-NEXT: Index: 3
# CHECK-NEXT: Offset: 0x10
-# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
+# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# CHECK-NEXT: Index: 5
# CHECK-NEXT: Offset: 0x1C
-# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_REL_SLEB
+# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_REL_SLEB64
# CHECK-NEXT: Index: 6
# CHECK-NEXT: Offset: 0x22
-# CHECK-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
+# CHECK-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# CHECK-NEXT: Index: 8
# CHECK-NEXT: Offset: 0x31
-# CHECK-NEXT: - Type: R_WASM_TABLE_INDEX_REL_SLEB
+# CHECK-NEXT: - Type: R_WASM_TABLE_INDEX_REL_SLEB64
# CHECK-NEXT: Index: 9
# CHECK-NEXT: Offset: 0x37
# CHECK-NEXT: Functions:
# CHECK-NEXT: - Index: 1
# CHECK-NEXT: Locals: []
-# CHECK-NEXT: Body: 2380808080002802000B
+# CHECK-NEXT: Body: 2382808080002802000B
# CHECK-NEXT: - Index: 2
# CHECK-NEXT: Locals: []
-# CHECK-NEXT: Body: 2381808080002802000B
+# CHECK-NEXT: Body: 2383808080002802000B
# CHECK-NEXT: - Index: 3
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 23808080800042808080808080808080007C0B
# CHECK-NEXT: - Index: 4
# CHECK-NEXT: Locals: []
-# CHECK-NEXT: Body: 23808080800042808080808080808080007C0B
+# CHECK-NEXT: Body: 23818080800042808080808080808080007C0B
# CHECK-NEXT: - Index: 5
# CHECK-NEXT: Locals: []
# CHECK-NEXT: Body: 41000B
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Function: 3
# CHECK-NEXT: - Index: 5
-# CHECK-NEXT: Kind: DATA
+# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: Name: __memory_base
# CHECK-NEXT: Flags: [ UNDEFINED ]
+# CHECK-NEXT: Global: 0
# CHECK-NEXT: - Index: 6
# CHECK-NEXT: Kind: DATA
# CHECK-NEXT: Name: .L.hidden_data
# CHECK-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-NEXT: Function: 4
# CHECK-NEXT: - Index: 8
-# CHECK-NEXT: Kind: DATA
+# CHECK-NEXT: Kind: GLOBAL
# CHECK-NEXT: Name: __table_base
# CHECK-NEXT: Flags: [ UNDEFINED ]
+# CHECK-NEXT: Global: 1
# CHECK-NEXT: - Index: 9
# CHECK-NEXT: Kind: FUNCTION
# CHECK-NEXT: Name: hidden_func
-# RUN: llvm-mc -show-encoding -triple=wasm32-unknown-unknown -mattr=+simd128 < %s | FileCheck %s
+# RUN: llvm-mc -no-type-check -show-encoding -triple=wasm32-unknown-unknown -mattr=+simd128 < %s | FileCheck %s
main:
.functype main () -> ()
end_function
foo2:
- .functype foo2 () -> ()
+ .functype foo2 () -> (i32)
+ i32.const 0
+ i32.const 0
# REF: return_call_indirect __indirect_function_table, (i32) -> (i32) # encoding: [0x13,
# CHECK: return_call_indirect (i32) -> (i32) # encoding: [0x13,
# CHECK-NEXT: fixup A - offset: 1, value: .Ltypeindex0@TYPEINDEX, kind: fixup_uleb128_i32
# RUN: llvm-mc -triple=wasm32-unknown-unknown -filetype=obj -o %t.o < %s
# RUN: obj2yaml %t.o | FileCheck %s --check-prefix=CHECK-OBJ
+.globaltype __tls_base, i32
+
tls_store:
.functype tls_store (i32) -> ()
# CHECK: global.get __tls_base
# CHECK-NEXT: i32.const tls1@TLSREL
# CHECK-NEXT: i32.add
# CHECK-NEXT: i32.store 0
+ local.get 0
global.get __tls_base
i32.const tls1@TLSREL
i32.add
# CHECK-OBJ: - Type: CODE
# CHECK-OBJ-NEXT: Relocations:
-# CHECK-OBJ-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB
+# CHECK-OBJ-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# CHECK-OBJ-NEXT: Index: 1
-# CHECK-OBJ-NEXT: Offset: 0x4
+# CHECK-OBJ-NEXT: Offset: 0x6
# CHECK-OBJ-NEXT: - Type: R_WASM_MEMORY_ADDR_TLS_SLEB
# CHECK-OBJ-NEXT: Index: 2
-# CHECK-OBJ-NEXT: Offset: 0xA
+# CHECK-OBJ-NEXT: Offset: 0xC
# CHECK-OBJ: - Type: CUSTOM
# CHECK-OBJ-NEXT: Name: linking
# CHECK-OBJ-NEXT: Flags: [ BINDING_LOCAL ]
# CHECK-OBJ-NEXT: Function: 0
# CHECK-OBJ-NEXT: - Index: 1
-# CHECK-OBJ-NEXT: Kind: DATA
+# CHECK-OBJ-NEXT: Kind: GLOBAL
# CHECK-OBJ-NEXT: Name: __tls_base
# CHECK-OBJ-NEXT: Flags: [ UNDEFINED ]
+# CHECK-OBJ-NEXT: Global: 0
# CHECK-OBJ-NEXT: - Index: 2
# CHECK-OBJ-NEXT: Kind: DATA
# CHECK-OBJ-NEXT: Name: tls1
test0:
.functype test0 (i32) -> (i32)
- call_indirect (f64) -> (f64)
+ f64.const 1.0
+ local.get 0
+ call_indirect (f64) -> (i32)
end_function
# CHECK: .text
# CHECK-LABEL: test0:
# CHECK-NEXT: .functype test0 (i32) -> (i32)
-# CHECK-NEXT: call_indirect __indirect_function_table, (f64) -> (f64)
+# CHECK: call_indirect __indirect_function_table, (f64) -> (i32)
# CHECK-NEXT: end_function
# BIN: --- !WASM
# BIN-NEXT: ParamTypes:
# BIN-NEXT: - F64
# BIN-NEXT: ReturnTypes:
-# BIN-NEXT: - F64
+# BIN-NEXT: - I32
# BIN-NEXT: - Type: IMPORT
# BIN-NEXT: Imports:
# BIN-NEXT: - Module: env
# BIN-NEXT: Relocations:
# BIN-NEXT: - Type: R_WASM_TYPE_INDEX_LEB
# BIN-NEXT: Index: 1
-# BIN-NEXT: Offset: 0x4
+# BIN-NEXT: Offset: 0xF
# BIN-NEXT: - Type: R_WASM_TABLE_NUMBER_LEB
# BIN-NEXT: Index: 1
-# BIN-NEXT: Offset: 0x9
+# BIN-NEXT: Offset: 0x14
# BIN-NEXT: Functions:
# BIN-NEXT: - Index: 0
# BIN-NEXT: Locals: []
-# BIN-NEXT: Body: 11818080800080808080000B
+# BIN-NEXT: Body: 44000000000000F03F200011818080800080808080000B
# BIN-NEXT: - Type: CUSTOM
# BIN-NEXT: Name: linking
# BIN-NEXT: Version: 2
# Most of our other tests are for wasm32, this one adds some wasm64 specific tests.
+.globaltype myglob64, i64
+.globaltype __stack_pointer, i64
+
test:
.functype test (i64) -> ()
.local i64
### basic stores
- f32.const 0.0
i64.const 0 # get i64 from constant.
+ f32.const 0.0
f32.store 0
- f32.const 0.0
local.get 0 # get i64 from local.
+ f32.const 0.0
f32.store 0
- f32.const 0.0
i64.const .L.str # get i64 relocatable.
+ f32.const 0.0
f32.store 0
- f32.const 0.0
global.get myglob64 # get i64 from global
+ f32.const 0.0
f32.store 0
- f32.const 0.0
i64.const 0
+ f32.const 0.0
f32.store .L.str # relocatable offset!
### 64-bit SP
.int64 .L.str # relocatable inside data.
.size .L.str, 24
- .globaltype myglob64, i64
- .globaltype __stack_pointer, i64
+# CHECK: .globaltype myglob64, i64
# CHECK: .functype test (i64) -> ()
# CHECK-NEXT: .local i64
# CHECK-NEXT: drop
-# CHECK: f32.const 0x0p0
-# CHECK-NEXT: i64.const 0
+# CHECK: i64.const 0
+# CHECK-NEXT: f32.const 0x0p0
# CHECK-NEXT: f32.store 0
-# CHECK: f32.const 0x0p0
-# CHECK-NEXT: local.get 0
+# CHECK: local.get 0
+# CHECK-NEXT: f32.const 0x0p0
# CHECK-NEXT: f32.store 0
-# CHECK: f32.const 0x0p0
-# CHECK-NEXT: i64.const .L.str
+# CHECK: i64.const .L.str
+# CHECK-NEXT: f32.const 0x0p0
# CHECK-NEXT: f32.store 0
-# CHECK: f32.const 0x0p0
-# CHECK-NEXT: global.get myglob64
+# CHECK: global.get myglob64
+# CHECK-NEXT: f32.const 0x0p0
# CHECK-NEXT: f32.store 0
-# CHECK: f32.const 0x0p0
-# CHECK-NEXT: i64.const 0
+# CHECK: i64.const 0
+# CHECK-NEXT: f32.const 0x0p0
# CHECK-NEXT: f32.store .L.str
# CHECK-NEXT: .int64 .L.str
# CHECK-NEXT: .size .L.str, 24
-# CHECK: .globaltype myglob64, i64
-
# BIN: --- !WASM
# BIN-NEXT: Offset: 0x2F
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_SLEB64
# BIN-NEXT: Index: 1
-# BIN-NEXT: Offset: 0x54
+# BIN-NEXT: Offset: 0x4F
# BIN-NEXT: - Type: R_WASM_GLOBAL_INDEX_LEB
# BIN-NEXT: Index: 2
-# BIN-NEXT: Offset: 0x67
+# BIN-NEXT: Offset: 0x62
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_LEB64
# BIN-NEXT: Index: 1
# BIN-NEXT: Offset: 0x78
# BIN-NEXT: Locals:
# BIN-NEXT: - Type: I64
# BIN-NEXT: Count: 1
-# BIN-NEXT: Body: 42002A02001A20002A02001A42808080808080808080002A02001A2380808080002A02001A42002A02808080808080808080001A4300000000420038020043000000002000380200430000000042808080808080808080003802004300000000238080808000380200430000000042003802808080808080808080002381808080001A0B
+# BIN-NEXT: Body: 42002A02001A20002A02001A42808080808080808080002A02001A2380808080002A02001A42002A02808080808080808080001A4200430000000038020020004300000000380200428080808080808080800043000000003802002380808080004300000000380200420043000000003802808080808080808080002381808080001A0B
# BIN-NEXT: - Type: DATA
# BIN-NEXT: Relocations:
# BIN-NEXT: - Type: R_WASM_MEMORY_ADDR_I64
# 'bar_alias' is weak alias of global variable 'bar'
# Generates two exports of the same function, one of them weak
+.functype foo_alias () -> (i32)
+
foo:
.hidden foo
.globl foo
"//llvm/lib/Target/WebAssembly/Utils",
]
include_dirs = [ ".." ]
- sources = [ "WebAssemblyAsmParser.cpp" ]
+ sources = [ "WebAssemblyAsmParser.cpp", "WebAssemblyAsmTypeCheck.cpp" ]
}
"WebAssemblyLowerEmscriptenEHSjLj.cpp",
"WebAssemblyLowerGlobalDtors.cpp",
"WebAssemblyMCInstLower.cpp",
+ "WebAssemblyMCLowerPrePass.cpp",
"WebAssemblyMachineFunctionInfo.cpp",
"WebAssemblyMemIntrinsicResults.cpp",
"WebAssemblyNullifyDebugValueLists.cpp",