llvm.org GIT mirror llvm / 561fb73
[WebAssembly] Emit type signatures for declared functions Under emscripten, C code can take the address of a function implemented in Javascript (which is exposed via an import in wasm). Because imports do not have linear memory address in wasm, we need to generate a thunk to be the target of the indirect call; it call the import directly. To make this possible, LLVM needs to emit the type signatures for these functions, because they may not be called directly or referred to other than where the address is taken. This uses s new .s directive (.functype) which specifies the signature. Differential Revision: http://reviews.llvm.org/D20891 Re-apply r271599 but instead of bailing with an error when a declared function has multiple returns, replace it with a pointer argument. Also add the test case I forgot to 'git add' last time around. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@271703 91177308-0d34-0410-b5e6-96231b3b80d8 Derek Schuff 4 years ago
4 changed file(s) with 129 addition(s) and 10 deletion(s). Raw diff Collapse all Expand all
6363
6464 void WebAssemblyTargetAsmStreamer::emitEndFunc() { OS << "\t.endfunc\n"; }
6565
66 void WebAssemblyTargetAsmStreamer::emitIndirectFunctionType(
67 StringRef name, SmallVectorImpl &SignatureVTs, size_t NumResults) {
68 OS << "\t.functype\t" << name;
69 if (NumResults == 0) OS << ", void";
70 for (auto Ty : SignatureVTs) {
71 OS << ", " << WebAssembly::TypeToString(Ty);
72 }
73 OS << "\n";
74 }
75
6676 // FIXME: What follows is not the real binary encoding.
6777
6878 static void EncodeTypes(MCStreamer &Streamer, ArrayRef Types) {
3636 virtual void emitLocal(ArrayRef Types) = 0;
3737 /// .endfunc
3838 virtual void emitEndFunc() = 0;
39 /// .functype
40 virtual void emitIndirectFunctionType(StringRef name,
41 SmallVectorImpl &SignatureVTs,
42 size_t NumResults) {
43 llvm_unreachable("emitIndirectFunctionType not implemented");
44 }
3945 };
4046
4147 /// This part is for ascii assembly output
4955 void emitResult(ArrayRef Types) override;
5056 void emitLocal(ArrayRef Types) override;
5157 void emitEndFunc() override;
58 void emitIndirectFunctionType(StringRef name,
59 SmallVectorImpl &SignatureVTs,
60 size_t NumResults) override;
5261 };
5362
5463 /// This part is for ELF object output
6666 // AsmPrinter Implementation.
6767 //===------------------------------------------------------------------===//
6868
69 void EmitEndOfAsmFile(Module &M) override;
6970 void EmitJumpTableInfo() override;
7071 void EmitConstantPool() override;
7172 void EmitFunctionBodyStart() override;
123124 //===----------------------------------------------------------------------===//
124125 // WebAssemblyAsmPrinter Implementation.
125126 //===----------------------------------------------------------------------===//
126
127 void WebAssemblyAsmPrinter::EmitConstantPool() {
128 assert(MF->getConstantPool()->getConstants().empty() &&
129 "WebAssembly disables constant pools");
130 }
131
132 void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
133 // Nothing to do; jump tables are incorporated into the instruction stream.
134 }
135
136127 static void ComputeLegalValueVTs(const Function &F, const TargetMachine &TM,
137128 Type *Ty, SmallVectorImpl &ValueVTs) {
138129 const DataLayout &DL(F.getParent()->getDataLayout());
147138 for (unsigned i = 0; i != NumRegs; ++i)
148139 ValueVTs.push_back(RegisterVT);
149140 }
141 }
142
143 void WebAssemblyAsmPrinter::EmitEndOfAsmFile(Module &M) {
144 for (const auto &F : M) {
145 // Emit function type info for all undefined functions
146 if (F.isDeclarationForLinker() && !F.isIntrinsic()) {
147 SmallVector SignatureVTs;
148 ComputeLegalValueVTs(F, TM, F.getReturnType(), SignatureVTs);
149 size_t NumResults = SignatureVTs.size();
150 if (SignatureVTs.size() > 1) {
151 // WebAssembly currently can't lower returns of multiple values without
152 // demoting to sret (see WebAssemblyTargetLowering::CanLowerReturn). So
153 // replace multiple return values with a pointer parameter.
154 SignatureVTs.clear();
155 SignatureVTs.push_back(
156 MVT::getIntegerVT(M.getDataLayout().getPointerSizeInBits()));
157 NumResults = 0;
158 }
159
160 for (auto &Arg : F.args()) {
161 ComputeLegalValueVTs(F, TM, Arg.getType(), SignatureVTs);
162 }
163
164 getTargetStreamer()->emitIndirectFunctionType(F.getName(), SignatureVTs,
165 NumResults);
166 }
167 }
168 }
169
170 void WebAssemblyAsmPrinter::EmitConstantPool() {
171 assert(MF->getConstantPool()->getConstants().empty() &&
172 "WebAssembly disables constant pools");
173 }
174
175 void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
176 // Nothing to do; jump tables are incorporated into the instruction stream.
150177 }
151178
152179 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
0 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs | FileCheck %s
1 ; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -verify-machineinstrs -fast-isel | FileCheck %s
2
3 ; ModuleID = 'test/dot_s/indirect-import.c'
4 source_filename = "test/dot_s/indirect-import.c"
5 target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
6 target triple = "wasm32"
7
8 %struct.big = type { float, double, i32 }
9
10 ; Function Attrs: nounwind
11 ; CHECK: bar:
12 define hidden i32 @bar() #0 {
13 entry:
14 %fd = alloca float (double)*, align 4
15 %vj = alloca void (i64)*, align 4
16 %v = alloca void ()*, align 4
17 %ijidf = alloca i32 (i64, i32, double, float)*, align 4
18 %vs = alloca void (%struct.big*)*, align 4
19 %s = alloca void (%struct.big*)*, align 4
20
21 ; CHECK: i32.const {{.+}}=, extern_fd@FUNCTION
22 store float (double)* @extern_fd, float (double)** %fd, align 4
23 ; CHECK: i32.const {{.+}}=, extern_vj@FUNCTION
24 store void (i64)* @extern_vj, void (i64)** %vj, align 4
25 %0 = load void (i64)*, void (i64)** %vj, align 4
26 call void %0(i64 1)
27
28 ; CHECK: i32.const {{.+}}=, extern_v@FUNCTION
29 store void ()* @extern_v, void ()** %v, align 4
30 %1 = load void ()*, void ()** %v, align 4
31 call void %1()
32
33 ; CHECK: i32.const {{.+}}=, extern_ijidf@FUNCTION
34 store i32 (i64, i32, double, float)* @extern_ijidf, i32 (i64, i32, double, float)** %ijidf, align 4
35 %2 = load i32 (i64, i32, double, float)*, i32 (i64, i32, double, float)** %ijidf, align 4
36 %call = call i32 %2(i64 1, i32 2, double 3.000000e+00, float 4.000000e+00)
37
38 ; CHECK: i32.const {{.+}}=, extern_struct@FUNCTION
39 store void (%struct.big*)* @extern_struct, void (%struct.big*)** %vs, align 4
40
41 ; CHECK: i32.const {{.+}}=, extern_sret@FUNCTION
42 store void (%struct.big*)* @extern_sret, void (%struct.big*)** %s, align 4
43 %3 = load float (double)*, float (double)** %fd, align 4
44 %4 = ptrtoint float (double)* %3 to i32
45 ret i32 %4
46 }
47
48 declare float @extern_fd(double) #1
49
50 declare void @extern_vj(i64) #1
51
52 declare void @extern_v() #1
53
54 declare i32 @extern_ijidf(i64, i32, double, float) #1
55
56 declare void @extern_struct(%struct.big* byval align 8) #1
57
58 declare void @extern_sret(%struct.big* sret) #1
59
60 declare i128 @extern_i128ret(i64) #1
61
62 attributes #0 = { nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
63 attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "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" }
64
65
66 ; CHECK: .functype extern_fd, f32, f64
67 ; CHECK: .functype extern_vj, void, i64
68 ; CHECK: .functype extern_v, void
69 ; CHECK: .functype extern_ijidf, i32, i64, i32, f64, f32
70 ; CHECK: .functype extern_struct, void, i32
71 ; CHECK: .functype extern_sret, void, i32
72 ; CHECK: .functype extern_i128ret, void, i32, i64