1/*
2 * Copyright 2017 WebAssembly Community Group participants
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//
18// Instruments the build with code to intercept all memory reads and writes.
19// This can be useful in building tools that analyze memory access behaviour.
20//
21// Before:
22// (i32.load8_s align=1 offset=2 (i32.const 3))
23//
24// After:
25// (call $load_val_i32
26// (i32.const n) // ID
27// (i32.load8_s align=1 offset=2
28// (call $load_ptr
29// (i32.const n) // ID
30// (i32.const 1) // bytes
31// (i32.const 2) // offset
32// (i32.const 3) // address
33// )
34// )
35// )
36// Stores: store(id, bytes, offset, address) => address
37//
38// Before:
39// (i32.store8 align=1 offset=2 (i32.const 3) (i32.const 4))
40//
41// After:
42// (i32.store16 align=1 offset=2
43// (call $store_ptr
44// (i32.const n) // ID
45// (i32.const 1) // bytes
46// (i32.const 2) // offset
47// (i32.const 3) // address
48// )
49// (call $store_val_i32
50// (i32.const n) // ID
51// (i32.const 4)
52// )
53// )
54//
55// GC struct and array operations are similarly instrumented, but without their
56// pointers (which are references), and we only log MVP wasm types (i.e., not
57// references).
58//
59
60#include "asmjs/shared-constants.h"
61#include "shared-constants.h"
62#include <pass.h>
63#include <wasm-builder.h>
64#include <wasm.h>
65
66namespace wasm {
67
68static Name load_ptr("load_ptr");
69static Name load_val_i32("load_val_i32");
70static Name load_val_i64("load_val_i64");
71static Name load_val_f32("load_val_f32");
72static Name load_val_f64("load_val_f64");
73static Name store_ptr("store_ptr");
74static Name store_val_i32("store_val_i32");
75static Name store_val_i64("store_val_i64");
76static Name store_val_f32("store_val_f32");
77static Name store_val_f64("store_val_f64");
78static Name struct_get_val_i32("struct_get_val_i32");
79static Name struct_get_val_i64("struct_get_val_i64");
80static Name struct_get_val_f32("struct_get_val_f32");
81static Name struct_get_val_f64("struct_get_val_f64");
82static Name struct_set_val_i32("struct_set_val_i32");
83static Name struct_set_val_i64("struct_set_val_i64");
84static Name struct_set_val_f32("struct_set_val_f32");
85static Name struct_set_val_f64("struct_set_val_f64");
86static Name array_get_val_i32("array_get_val_i32");
87static Name array_get_val_i64("array_get_val_i64");
88static Name array_get_val_f32("array_get_val_f32");
89static Name array_get_val_f64("array_get_val_f64");
90static Name array_set_val_i32("array_set_val_i32");
91static Name array_set_val_i64("array_set_val_i64");
92static Name array_set_val_f32("array_set_val_f32");
93static Name array_set_val_f64("array_set_val_f64");
94static Name array_get_index("array_get_index");
95static Name array_set_index("array_set_index");
96
97// TODO: Add support for atomicRMW/cmpxchg
98
99struct InstrumentMemory : public WalkerPass<PostWalker<InstrumentMemory>> {
100 void visitLoad(Load* curr) {
101 id++;
102 Builder builder(*getModule());
103 auto mem = getModule()->getMemory(curr->memory);
104 auto indexType = mem->indexType;
105 auto offset = builder.makeConstPtr(val: curr->offset.addr, indexType: indexType);
106 curr->ptr = builder.makeCall(load_ptr,
107 {builder.makeConst(x: int32_t(id)),
108 builder.makeConst(x: int32_t(curr->bytes)),
109 offset,
110 curr->ptr},
111 indexType);
112 Name target;
113 switch (curr->type.getBasic()) {
114 case Type::i32:
115 target = load_val_i32;
116 break;
117 case Type::i64:
118 target = load_val_i64;
119 break;
120 case Type::f32:
121 target = load_val_f32;
122 break;
123 case Type::f64:
124 target = load_val_f64;
125 break;
126 default:
127 return; // TODO: other types, unreachable, etc.
128 }
129 replaceCurrent(builder.makeCall(
130 target, {builder.makeConst(x: int32_t(id)), curr}, curr->type));
131 }
132
133 void visitStore(Store* curr) {
134 id++;
135 Builder builder(*getModule());
136 auto mem = getModule()->getMemory(curr->memory);
137 auto indexType = mem->indexType;
138 auto offset = builder.makeConstPtr(val: curr->offset.addr, indexType: indexType);
139 curr->ptr = builder.makeCall(store_ptr,
140 {builder.makeConst(x: int32_t(id)),
141 builder.makeConst(x: int32_t(curr->bytes)),
142 offset,
143 curr->ptr},
144 indexType);
145 Name target;
146 switch (curr->value->type.getBasic()) {
147 case Type::i32:
148 target = store_val_i32;
149 break;
150 case Type::i64:
151 target = store_val_i64;
152 break;
153 case Type::f32:
154 target = store_val_f32;
155 break;
156 case Type::f64:
157 target = store_val_f64;
158 break;
159 default:
160 return; // TODO: other types, unreachable, etc.
161 }
162 curr->value = builder.makeCall(
163 target, {builder.makeConst(x: int32_t(id)), curr->value}, curr->value->type);
164 }
165
166 void visitStructGet(StructGet* curr) {
167 Builder builder(*getModule());
168 Name target;
169 if (curr->type == Type::i32) {
170 target = struct_get_val_i32;
171 } else if (curr->type == Type::i64) {
172 target = struct_get_val_i64;
173 } else if (curr->type == Type::f32) {
174 target = struct_get_val_f32;
175 } else if (curr->type == Type::f64) {
176 target = struct_get_val_f64;
177 } else {
178 return; // TODO: other types, unreachable, etc.
179 }
180 replaceCurrent(builder.makeCall(
181 target, {builder.makeConst(x: int32_t(id++)), curr}, curr->type));
182 }
183
184 void visitStructSet(StructSet* curr) {
185 Builder builder(*getModule());
186 Name target;
187 if (curr->value->type == Type::i32) {
188 target = struct_set_val_i32;
189 } else if (curr->value->type == Type::i64) {
190 target = struct_set_val_i64;
191 } else if (curr->value->type == Type::f32) {
192 target = struct_set_val_f32;
193 } else if (curr->value->type == Type::f64) {
194 target = struct_set_val_f64;
195 } else {
196 return; // TODO: other types, unreachable, etc.
197 }
198 curr->value =
199 builder.makeCall(target,
200 {builder.makeConst(x: int32_t(id++)), curr->value},
201 curr->value->type);
202 }
203
204 void visitArrayGet(ArrayGet* curr) {
205 Builder builder(*getModule());
206 curr->index =
207 builder.makeCall(array_get_index,
208 {builder.makeConst(x: int32_t(id++)), curr->index},
209 Type::i32);
210 Name target;
211 if (curr->type == Type::i32) {
212 target = array_get_val_i32;
213 } else if (curr->type == Type::i64) {
214 target = array_get_val_i64;
215 } else if (curr->type == Type::f32) {
216 target = array_get_val_f32;
217 } else if (curr->type == Type::f64) {
218 target = array_get_val_f64;
219 } else {
220 return; // TODO: other types, unreachable, etc.
221 }
222 replaceCurrent(builder.makeCall(
223 target, {builder.makeConst(x: int32_t(id++)), curr}, curr->type));
224 }
225
226 void visitArraySet(ArraySet* curr) {
227 Builder builder(*getModule());
228 curr->index =
229 builder.makeCall(array_set_index,
230 {builder.makeConst(x: int32_t(id++)), curr->index},
231 Type::i32);
232 Name target;
233 if (curr->value->type == Type::i32) {
234 target = array_set_val_i32;
235 } else if (curr->value->type == Type::i64) {
236 target = array_set_val_i64;
237 } else if (curr->value->type == Type::f32) {
238 target = array_set_val_f32;
239 } else if (curr->value->type == Type::f64) {
240 target = array_set_val_f64;
241 } else {
242 return; // TODO: other types, unreachable, etc.
243 }
244 curr->value =
245 builder.makeCall(target,
246 {builder.makeConst(x: int32_t(id++)), curr->value},
247 curr->value->type);
248 }
249
250 void visitModule(Module* curr) {
251 auto indexType =
252 curr->memories.empty() ? Type::i32 : curr->memories[0]->indexType;
253
254 // Load.
255 addImport(
256 curr, name: load_ptr, params: {Type::i32, Type::i32, indexType, indexType}, results: indexType);
257 addImport(curr, load_val_i32, {Type::i32, Type::i32}, Type::i32);
258 addImport(curr, load_val_i64, {Type::i32, Type::i64}, Type::i64);
259 addImport(curr, load_val_f32, {Type::i32, Type::f32}, Type::f32);
260 addImport(curr, load_val_f64, {Type::i32, Type::f64}, Type::f64);
261
262 // Store.
263 addImport(
264 curr, name: store_ptr, params: {Type::i32, Type::i32, indexType, indexType}, results: indexType);
265 addImport(curr, store_val_i32, {Type::i32, Type::i32}, Type::i32);
266 addImport(curr, store_val_i64, {Type::i32, Type::i64}, Type::i64);
267 addImport(curr, store_val_f32, {Type::i32, Type::f32}, Type::f32);
268 addImport(curr, store_val_f64, {Type::i32, Type::f64}, Type::f64);
269
270 if (curr->features.hasGC()) {
271 // Struct get/set.
272 addImport(curr, struct_get_val_i32, {Type::i32, Type::i32}, Type::i32);
273 addImport(curr, struct_get_val_i64, {Type::i32, Type::i64}, Type::i64);
274 addImport(curr, struct_get_val_f32, {Type::i32, Type::f32}, Type::f32);
275 addImport(curr, struct_get_val_f64, {Type::i32, Type::f64}, Type::f64);
276 addImport(curr, struct_set_val_i32, {Type::i32, Type::i32}, Type::i32);
277 addImport(curr, struct_set_val_i64, {Type::i32, Type::i64}, Type::i64);
278 addImport(curr, struct_set_val_f32, {Type::i32, Type::f32}, Type::f32);
279 addImport(curr, struct_set_val_f64, {Type::i32, Type::f64}, Type::f64);
280
281 // Array get/set.
282 addImport(curr, array_get_val_i32, {Type::i32, Type::i32}, Type::i32);
283 addImport(curr, array_get_val_i64, {Type::i32, Type::i64}, Type::i64);
284 addImport(curr, array_get_val_f32, {Type::i32, Type::f32}, Type::f32);
285 addImport(curr, array_get_val_f64, {Type::i32, Type::f64}, Type::f64);
286 addImport(curr, array_set_val_i32, {Type::i32, Type::i32}, Type::i32);
287 addImport(curr, array_set_val_i64, {Type::i32, Type::i64}, Type::i64);
288 addImport(curr, array_set_val_f32, {Type::i32, Type::f32}, Type::f32);
289 addImport(curr, array_set_val_f64, {Type::i32, Type::f64}, Type::f64);
290 addImport(curr, array_get_index, {Type::i32, Type::i32}, Type::i32);
291 addImport(curr, array_set_index, {Type::i32, Type::i32}, Type::i32);
292 }
293 }
294
295private:
296 Index id;
297
298 void addImport(Module* curr, Name name, Type params, Type results) {
299 auto import = Builder::makeFunction(name, Signature(params, results), {});
300 import->module = ENV;
301 import->base = name;
302 curr->addFunction(std::move(import));
303 }
304};
305
306Pass* createInstrumentMemoryPass() { return new InstrumentMemory(); }
307
308} // namespace wasm
309

source code of dart_sdk/third_party/binaryen/src/src/passes/InstrumentMemory.cpp