llvm.org GIT mirror llvm / 456ed89
WebAssembly: generate load/store Summary: This handles all load/store operations that WebAssembly defines, and handles those necessary for C++ such as i1. I left a FIXME for outstanding features which aren't required for now. Reviewers: sunfish Subscribers: jfb, llvm-commits, dschuff git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@246500 91177308-0d34-0410-b5e6-96231b3b80d8 JF Bastien 5 years ago
8 changed file(s) with 409 addition(s) and 49 deletion(s). Raw diff Collapse all Expand all
3939 namespace {
4040
4141 class WebAssemblyAsmPrinter final : public AsmPrinter {
42 bool hasAddr64;
4243 const WebAssemblyInstrInfo *TII;
4344
4445 public:
5960 }
6061
6162 bool runOnMachineFunction(MachineFunction &MF) override {
62 TII = MF.getSubtarget().getInstrInfo();
63 const auto &Subtarget = MF.getSubtarget();
64 hasAddr64 = Subtarget.hasAddr64();
65 TII = Subtarget.getInstrInfo();
6366 return AsmPrinter::runOnMachineFunction(MF);
6467 }
6568
9598 }
9699
97100 static std::string toSymbol(StringRef S) { return ("$" + S).str(); }
98
99 static const char *toString(const Type *Ty) {
100 switch (Ty->getTypeID()) {
101 default: break;
102 case Type::FloatTyID: return "f32";
103 case Type::DoubleTyID: return "f64";
104 case Type::IntegerTyID:
105 switch (Ty->getIntegerBitWidth()) {
106 case 32: return "i32";
107 case 64: return "i64";
108 default: break;
109 }
110 }
111 DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n');
112 llvm_unreachable("invalid type");
113 return "";
114 }
115101
116102 static std::string toString(const APFloat &FP) {
117103 static const size_t BufBytes = 128;
129115 assert(Written < BufBytes);
130116 return buf;
131117 }
118
119 static const char *toString(const Type *Ty, bool hasAddr64) {
120 switch (Ty->getTypeID()) {
121 default: break;
122 // Treat all pointers as the underlying integer into linear memory.
123 case Type::PointerTyID: return hasAddr64 ? "i64" : "i32";
124 case Type::FloatTyID: return "f32";
125 case Type::DoubleTyID: return "f64";
126 case Type::IntegerTyID:
127 switch (Ty->getIntegerBitWidth()) {
128 case 8: return "i8";
129 case 16: return "i16";
130 case 32: return "i32";
131 case 64: return "i64";
132 default: break;
133 }
134 }
135 DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n');
136 llvm_unreachable("invalid type");
137 return "";
138 }
139
132140
133141 //===----------------------------------------------------------------------===//
134142 // WebAssemblyAsmPrinter Implementation.
185193 return;
186194 }
187195
188 OS << "(global " << toSymbol(Name) << ' ' << toString(Init->getType()) << ' ';
196 OS << "(global " << toSymbol(Name) << ' '
197 << toString(Init->getType(), hasAddr64) << ' ';
189198 if (const auto *C = dyn_cast(Init)) {
190199 assert(C->getBitWidth() <= 64 && "Printing wider types unimplemented");
191200 OS << C->getZExtValue();
227236 raw_svector_ostream OS(Str);
228237 const Function *F = MF->getFunction();
229238 for (const Argument &A : F->args())
230 OS << " (param " << toString(A.getType()) << ')';
239 OS << " (param " << toString(A.getType(), hasAddr64) << ')';
231240 const Type *Rt = F->getReturnType();
232241 if (!Rt->isVoidTy())
233 OS << " (result " << toString(Rt) << ')';
242 OS << " (result " << toString(Rt, hasAddr64) << ')';
234243 OS << '\n';
235244 OutStreamer->EmitRawText(OS.str());
236245 }
112112 // Compute derived properties from the register classes.
113113 computeRegisterProperties(Subtarget->getRegisterInfo());
114114
115 // FIXME: many setOperationAction are missing...
116
117115 setOperationAction(ISD::GlobalAddress, MVTPtr, Custom);
118116
119117 for (auto T : {MVT::f32, MVT::f64}) {
153151 setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
154152 setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);
155153 setOperationAction(ISD::DYNAMIC_STACKALLOC, MVTPtr, Expand);
154
155 // WebAssembly doesn't have:
156 // - Floating-point extending loads.
157 // - Floating-point truncating stores.
158 // - i1 extending loads.
159 setLoadExtAction(ISD::EXTLOAD, MVT::f32, MVT::f64, Expand);
160 setTruncStoreAction(MVT::f64, MVT::f32, Expand);
161 for (auto T : MVT::integer_valuetypes())
162 for (auto Ext : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD})
163 setLoadExtAction(Ext, T, MVT::i1, Promote);
156164 }
157165
158166 FastISel *WebAssemblyTargetLowering::createFastISel(
1313
1414 /*
1515 * TODO(jfb): Add the following.
16 * Each has optional alignment and immediate byte offset.
17 *
18 * int32.load_sx[int8]: sign-extend to int32
19 * int32.load_sx[int16]: sign-extend to int32
20 * int32.load_zx[int8]: zero-extend to int32
21 * int32.load_zx[int16]: zero-extend to int32
22 * int32.load[int32]: (no conversion)
23 * int64.load_sx[int8]: sign-extend to int64
24 * int64.load_sx[int16]: sign-extend to int64
25 * int64.load_sx[int32]: sign-extend to int64
26 * int64.load_zx[int8]: zero-extend to int64
27 * int64.load_zx[int16]: zero-extend to int64
28 * int64.load_zx[int32]: zero-extend to int64
29 * int64.load[int64]: (no conversion)
30 * float32.load[float32]: (no conversion)
31 * float64.load[float64]: (no conversion)
32 *
33 * int32.store[int8]: wrap int32 to int8
34 * int32.store[int16]: wrap int32 to int16
35 * int32.store[int32]: (no conversion)
36 * int64.store[int8]: wrap int64 to int8
37 * int64.store[int16]: wrap int64 to int16
38 * int64.store[int32]: wrap int64 to int32
39 * int64.store[int64]: (no conversion)
40 * float32.store[float32]: (no conversion)
41 * float64.store[float64]: (no conversion)
4216 *
4317 * load_global: load the value of a given global variable
4418 * store_global: store a given value to a given global variable
4519 */
4620
21 // FIXME:
22 // - HasAddr64
23 // - WebAssemblyTargetLowering::isLegalAddressingMode
24 // - WebAssemblyTargetLowering having to do with atomics
25 // - Each has optional alignment and immediate byte offset.
26
27 // WebAssembly has i8/i16/i32/i64/f32/f64 memory types, but doesn't have i8/i16
28 // local types. These memory-only types instead zero- or sign-extend into local
29 // types when loading, and truncate when storing.
30
31 // Basic load.
32 def LOAD_I32_ : I<(outs Int32:$dst), (ins Int32:$addr),
33 [(set Int32:$dst, (load Int32:$addr))]>;
34 def LOAD_I64_ : I<(outs Int64:$dst), (ins Int32:$addr),
35 [(set Int64:$dst, (load Int32:$addr))]>;
36 def LOAD_F32_ : I<(outs Float32:$dst), (ins Int32:$addr),
37 [(set Float32:$dst, (load Int32:$addr))]>;
38 def LOAD_F64_ : I<(outs Float64:$dst), (ins Int32:$addr),
39 [(set Float64:$dst, (load Int32:$addr))]>;
40
41 // Extending load.
42 def LOAD_SX_I8_I32_ : I<(outs Int32:$dst), (ins Int32:$addr),
43 [(set Int32:$dst, (sextloadi8 Int32:$addr))]>;
44 def LOAD_ZX_I8_I32_ : I<(outs Int32:$dst), (ins Int32:$addr),
45 [(set Int32:$dst, (zextloadi8 Int32:$addr))]>;
46 def LOAD_SX_I16_I32_ : I<(outs Int32:$dst), (ins Int32:$addr),
47 [(set Int32:$dst, (sextloadi16 Int32:$addr))]>;
48 def LOAD_ZX_I16_I32_ : I<(outs Int32:$dst), (ins Int32:$addr),
49 [(set Int32:$dst, (zextloadi16 Int32:$addr))]>;
50 def LOAD_SX_I8_I64_ : I<(outs Int64:$dst), (ins Int32:$addr),
51 [(set Int64:$dst, (sextloadi8 Int32:$addr))]>;
52 def LOAD_ZX_I8_I64_ : I<(outs Int64:$dst), (ins Int32:$addr),
53 [(set Int64:$dst, (zextloadi8 Int32:$addr))]>;
54 def LOAD_SX_I16_I64_ : I<(outs Int64:$dst), (ins Int32:$addr),
55 [(set Int64:$dst, (sextloadi16 Int32:$addr))]>;
56 def LOAD_ZX_I16_I64_ : I<(outs Int64:$dst), (ins Int32:$addr),
57 [(set Int64:$dst, (zextloadi16 Int32:$addr))]>;
58 def LOAD_SX_I32_I64_ : I<(outs Int64:$dst), (ins Int32:$addr),
59 [(set Int64:$dst, (sextloadi32 Int32:$addr))]>;
60 def LOAD_ZX_I32_I64_ : I<(outs Int64:$dst), (ins Int32:$addr),
61 [(set Int64:$dst, (zextloadi32 Int32:$addr))]>;
62
63 // "Don't care" extending load become zero-extending load.
64 def : Pat<(i32 (extloadi8 Int32:$addr)), (LOAD_ZX_I8_I32_ $addr)>;
65 def : Pat<(i32 (extloadi16 Int32:$addr)), (LOAD_ZX_I16_I32_ $addr)>;
66 def : Pat<(i64 (extloadi8 Int32:$addr)), (LOAD_ZX_I8_I64_ $addr)>;
67 def : Pat<(i64 (extloadi16 Int32:$addr)), (LOAD_ZX_I16_I64_ $addr)>;
68 def : Pat<(i64 (extloadi32 Int32:$addr)), (LOAD_ZX_I32_I64_ $addr)>;
69
70 // Basic store.
71 // Note: WebAssembly inverts SelectionDAG's usual operand order.
72 def STORE_I32_ : I<(outs), (ins Int32:$addr, Int32:$val),
73 [(store Int32:$val, Int32:$addr)]>;
74 def STORE_I64_ : I<(outs), (ins Int32:$addr, Int64:$val),
75 [(store Int64:$val, Int32:$addr)]>;
76 def STORE_F32_ : I<(outs), (ins Int32:$addr, Float32:$val),
77 [(store Float32:$val, Int32:$addr)]>;
78 def STORE_F64_ : I<(outs), (ins Int32:$addr, Float64:$val),
79 [(store Float64:$val, Int32:$addr)]>;
80
81 // Truncating store.
82 def STORE_I8_I32 : I<(outs), (ins Int32:$addr, Int32:$val),
83 [(truncstorei8 Int32:$val, Int32:$addr)]>;
84 def STORE_I16_I32 : I<(outs), (ins Int32:$addr, Int32:$val),
85 [(truncstorei16 Int32:$val, Int32:$addr)]>;
86 def STORE_I8_I64 : I<(outs), (ins Int32:$addr, Int64:$val),
87 [(truncstorei8 Int64:$val, Int32:$addr)]>;
88 def STORE_I16_I64 : I<(outs), (ins Int32:$addr, Int64:$val),
89 [(truncstorei16 Int64:$val, Int32:$addr)]>;
90 def STORE_I32_I64 : I<(outs), (ins Int32:$addr, Int64:$val),
91 [(truncstorei32 Int64:$val, Int32:$addr)]>;
92
93 // Page size.
4794 def page_size_I32 : I<(outs Int32:$dst), (ins),
4895 [(set Int32:$dst, (int_wasm_page_size))]>,
4996 Requires<[HasAddr32]>;
0 ; RUN: llc < %s -asm-verbose=false | FileCheck %s
1
2 ; Test that extending loads are assembled properly.
3
4 target datalayout = "e-p:32:32-i64:64-v128:8:128-n32:64-S128"
5 target triple = "wasm32-unknown-unknown"
6
7 ; CHECK-LABEL: (func $sext_i8_i32
8 ; CHECK: (setlocal @1 (load_sx_i8_i32 @0))
9 define i32 @sext_i8_i32(i8 *%p) {
10 %v = load i8, i8* %p
11 %e = sext i8 %v to i32
12 ret i32 %e
13 }
14
15 ; CHECK-LABEL: (func $zext_i8_i32
16 ; CHECK: (setlocal @1 (load_zx_i8_i32 @0))
17 define i32 @zext_i8_i32(i8 *%p) {
18 %v = load i8, i8* %p
19 %e = zext i8 %v to i32
20 ret i32 %e
21 }
22
23 ; CHECK-LABEL: (func $sext_i16_i32
24 ; CHECK: (setlocal @1 (load_sx_i16_i32 @0))
25 define i32 @sext_i16_i32(i16 *%p) {
26 %v = load i16, i16* %p
27 %e = sext i16 %v to i32
28 ret i32 %e
29 }
30
31 ; CHECK-LABEL: (func $zext_i16_i32
32 ; CHECK: (setlocal @1 (load_zx_i16_i32 @0))
33 define i32 @zext_i16_i32(i16 *%p) {
34 %v = load i16, i16* %p
35 %e = zext i16 %v to i32
36 ret i32 %e
37 }
38
39 ; CHECK-LABEL: (func $sext_i8_i64
40 ; CHECK: (setlocal @1 (load_sx_i8_i64 @0))
41 define i64 @sext_i8_i64(i8 *%p) {
42 %v = load i8, i8* %p
43 %e = sext i8 %v to i64
44 ret i64 %e
45 }
46
47 ; CHECK-LABEL: (func $zext_i8_i64
48 ; CHECK: (setlocal @1 (load_zx_i8_i64 @0))
49 define i64 @zext_i8_i64(i8 *%p) {
50 %v = load i8, i8* %p
51 %e = zext i8 %v to i64
52 ret i64 %e
53 }
54
55 ; CHECK-LABEL: (func $sext_i16_i64
56 ; CHECK: (setlocal @1 (load_sx_i16_i64 @0))
57 define i64 @sext_i16_i64(i16 *%p) {
58 %v = load i16, i16* %p
59 %e = sext i16 %v to i64
60 ret i64 %e
61 }
62
63 ; CHECK-LABEL: (func $zext_i16_i64
64 ; CHECK: (setlocal @1 (load_zx_i16_i64 @0))
65 define i64 @zext_i16_i64(i16 *%p) {
66 %v = load i16, i16* %p
67 %e = zext i16 %v to i64
68 ret i64 %e
69 }
70
71 ; CHECK-LABEL: (func $sext_i32_i64
72 ; CHECK: (setlocal @1 (load_sx_i32_i64 @0))
73 define i64 @sext_i32_i64(i32 *%p) {
74 %v = load i32, i32* %p
75 %e = sext i32 %v to i64
76 ret i64 %e
77 }
78
79 ; CHECK-LABEL: (func $zext_i32_i64
80 ; CHECK: (setlocal @1 (load_zx_i32_i64 @0))
81 define i64 @zext_i32_i64(i32 *%p) {
82 %v = load i32, i32* %p
83 %e = zext i32 %v to i64
84 ret i64 %e
85 }
0 ; RUN: llc < %s -asm-verbose=false | FileCheck %s
1
2 ; Test that i1 extending loads and truncating stores are assembled properly.
3
4 target datalayout = "e-p:32:32-i64:64-v128:8:128-n32:64-S128"
5 target triple = "wasm32-unknown-unknown"
6
7 ; CHECK-LABEL: (func $load_unsigned_i1_i32
8 ; CHECK: (setlocal @1 (load_zx_i8_i32 @0))
9 ; CHECK-NEXT: (return @1)
10 define i32 @load_unsigned_i1_i32(i1* %p) {
11 %v = load i1, i1* %p
12 %e = zext i1 %v to i32
13 ret i32 %e
14 }
15
16 ; CHECK-LABEL: (func $load_signed_i1_i32
17 ; CHECK: (setlocal @1 (load_zx_i8_i32 @0))
18 ; CHECK-NEXT: (setlocal @2 (immediate 31))
19 ; CHECK-NEXT: (setlocal @3 (shl @1 @2))
20 ; CHECK-NEXT: (setlocal @4 (sar @3 @2))
21 ; CHECK-NEXT: (return @4)
22 define i32 @load_signed_i1_i32(i1* %p) {
23 %v = load i1, i1* %p
24 %e = sext i1 %v to i32
25 ret i32 %e
26 }
27
28 ; CHECK-LABEL: (func $load_unsigned_i1_i64
29 ; CHECK: (setlocal @1 (load_zx_i8_i64 @0))
30 ; CHECK-NEXT: (return @1)
31 define i64 @load_unsigned_i1_i64(i1* %p) {
32 %v = load i1, i1* %p
33 %e = zext i1 %v to i64
34 ret i64 %e
35 }
36
37 ; CHECK-LABEL: (func $load_signed_i1_i64
38 ; CHECK: (setlocal @1 (load_zx_i8_i64 @0))
39 ; CHECK-NEXT: (setlocal @2 (immediate 63))
40 ; CHECK-NEXT: (setlocal @3 (shl @1 @2))
41 ; CHECK-NEXT: (setlocal @4 (sar @3 @2))
42 ; CHECK-NEXT: (return @4)
43 define i64 @load_signed_i1_i64(i1* %p) {
44 %v = load i1, i1* %p
45 %e = sext i1 %v to i64
46 ret i64 %e
47 }
48
49 ; CHECK-LABEL: (func $store_i32_i1
50 ; CHECK: (setlocal @2 (immediate 1))
51 ; CHECK-NEXT: (setlocal @3 (and @1 @2))
52 ; CHECK-NEXT: (store_i8 @0 @3)
53 define void @store_i32_i1(i1* %p, i32 %v) {
54 %t = trunc i32 %v to i1
55 store i1 %t, i1* %p
56 ret void
57 }
58
59 ; CHECK-LABEL: (func $store_i64_i1
60 ; CHECK: (setlocal @2 (immediate 1))
61 ; CHECK-NEXT: (setlocal @3 (and @1 @2))
62 ; CHECK-NEXT: (store_i8 @0 @3)
63 define void @store_i64_i1(i1* %p, i64 %v) {
64 %t = trunc i64 %v to i1
65 store i1 %t, i1* %p
66 ret void
67 }
0 ; RUN: llc < %s -asm-verbose=false | FileCheck %s
1
2 ; Test that basic loads are assembled properly.
3
4 target datalayout = "e-p:32:32-i64:64-v128:8:128-n32:64-S128"
5 target triple = "wasm32-unknown-unknown"
6
7 ; CHECK-LABEL: (func $ldi32
8 ; CHECK-NEXT: (param i32) (result i32)
9 ; CHECK-NEXT: (setlocal @0 (argument 0))
10 ; CHECK-NEXT: (setlocal @1 (load_i32 @0))
11 ; CHECK-NEXT: (return @1)
12 define i32 @ldi32(i32 *%p) {
13 %v = load i32, i32* %p
14 ret i32 %v
15 }
16
17 ; CHECK-LABEL: (func $ldi64
18 ; CHECK-NEXT: (param i32) (result i64)
19 ; CHECK-NEXT: (setlocal @0 (argument 0))
20 ; CHECK-NEXT: (setlocal @1 (load_i64 @0))
21 ; CHECK-NEXT: (return @1)
22 define i64 @ldi64(i64 *%p) {
23 %v = load i64, i64* %p
24 ret i64 %v
25 }
26
27 ; CHECK-LABEL: (func $ldf32
28 ; CHECK-NEXT: (param i32) (result f32)
29 ; CHECK-NEXT: (setlocal @0 (argument 0))
30 ; CHECK-NEXT: (setlocal @1 (load_f32 @0))
31 ; CHECK-NEXT: (return @1)
32 define float @ldf32(float *%p) {
33 %v = load float, float* %p
34 ret float %v
35 }
36
37 ; CHECK-LABEL: (func $ldf64
38 ; CHECK-NEXT: (param i32) (result f64)
39 ; CHECK-NEXT: (setlocal @0 (argument 0))
40 ; CHECK-NEXT: (setlocal @1 (load_f64 @0))
41 ; CHECK-NEXT: (return @1)
42 define double @ldf64(double *%p) {
43 %v = load double, double* %p
44 ret double %v
45 }
0 ; RUN: llc < %s -asm-verbose=false | FileCheck %s
1
2 ; Test that truncating stores are assembled properly.
3
4 target datalayout = "e-p:32:32-i64:64-v128:8:128-n32:64-S128"
5 target triple = "wasm32-unknown-unknown"
6
7 ; CHECK-LABEL: (func $trunc_i8_i32
8 ; CHECK: (store_i8 @1 @0)
9 define void @trunc_i8_i32(i8 *%p, i32 %v) {
10 %t = trunc i32 %v to i8
11 store i8 %t, i8* %p
12 ret void
13 }
14
15 ; CHECK-LABEL: (func $trunc_i16_i32
16 ; CHECK: (store_i16 @1 @0)
17 define void @trunc_i16_i32(i16 *%p, i32 %v) {
18 %t = trunc i32 %v to i16
19 store i16 %t, i16* %p
20 ret void
21 }
22
23 ; CHECK-LABEL: (func $trunc_i8_i64
24 ; CHECK: (store_i8 @1 @0)
25 define void @trunc_i8_i64(i8 *%p, i64 %v) {
26 %t = trunc i64 %v to i8
27 store i8 %t, i8* %p
28 ret void
29 }
30
31 ; CHECK-LABEL: (func $trunc_i16_i64
32 ; CHECK: (store_i16 @1 @0)
33 define void @trunc_i16_i64(i16 *%p, i64 %v) {
34 %t = trunc i64 %v to i16
35 store i16 %t, i16* %p
36 ret void
37 }
38
39 ; CHECK-LABEL: (func $trunc_i32_i64
40 ; CHECK: (store_i32 @1 @0)
41 define void @trunc_i32_i64(i32 *%p, i64 %v) {
42 %t = trunc i64 %v to i32
43 store i32 %t, i32* %p
44 ret void
45 }
0 ; RUN: llc < %s -asm-verbose=false | FileCheck %s
1
2 ; Test that basic stores are assembled properly.
3
4 target datalayout = "e-p:32:32-i64:64-v128:8:128-n32:64-S128"
5 target triple = "wasm32-unknown-unknown"
6
7 ; CHECK-LABEL: (func $sti32
8 ; CHECK-NEXT: (param i32) (param i32)
9 ; CHECK-NEXT: (setlocal @0 (argument 1))
10 ; CHECK-NEXT: (setlocal @1 (argument 0))
11 ; CHECK-NEXT: (store_i32 @1 @0)
12 ; CHECK-NEXT: (return)
13 define void @sti32(i32 *%p, i32 %v) {
14 store i32 %v, i32* %p
15 ret void
16 }
17
18 ; CHECK-LABEL: (func $sti64
19 ; CHECK-NEXT: (param i32) (param i64)
20 ; CHECK-NEXT: (setlocal @0 (argument 1))
21 ; CHECK-NEXT: (setlocal @1 (argument 0))
22 ; CHECK-NEXT: (store_i64 @1 @0)
23 ; CHECK-NEXT: (return)
24 define void @sti64(i64 *%p, i64 %v) {
25 store i64 %v, i64* %p
26 ret void
27 }
28
29 ; CHECK-LABEL: (func $stf32
30 ; CHECK-NEXT: (param i32) (param f32)
31 ; CHECK-NEXT: (setlocal @0 (argument 1))
32 ; CHECK-NEXT: (setlocal @1 (argument 0))
33 ; CHECK-NEXT: (store_f32 @1 @0)
34 ; CHECK-NEXT: (return)
35 define void @stf32(float *%p, float %v) {
36 store float %v, float* %p
37 ret void
38 }
39
40 ; CHECK-LABEL: (func $stf64
41 ; CHECK-NEXT: (param i32) (param f64)
42 ; CHECK-NEXT: (setlocal @0 (argument 1))
43 ; CHECK-NEXT: (setlocal @1 (argument 0))
44 ; CHECK-NEXT: (store_f64 @1 @0)
45 ; CHECK-NEXT: (return)
46 define void @stf64(double *%p, double %v) {
47 store double %v, double* %p
48 ret void
49 }