| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef WASM_RUN_UTILS_H | |
| 6 #define WASM_RUN_UTILS_H | |
| 7 | |
| 8 #include <stdint.h> | |
| 9 #include <stdlib.h> | |
| 10 #include <string.h> | |
| 11 | |
| 12 #include "src/base/utils/random-number-generator.h" | |
| 13 | |
| 14 #include "src/compiler/graph-visualizer.h" | |
| 15 #include "src/compiler/js-graph.h" | |
| 16 #include "src/compiler/wasm-compiler.h" | |
| 17 | |
| 18 #include "src/wasm/ast-decoder.h" | |
| 19 #include "src/wasm/wasm-js.h" | |
| 20 #include "src/wasm/wasm-module.h" | |
| 21 #include "src/wasm/wasm-opcodes.h" | |
| 22 | |
| 23 #include "test/cctest/cctest.h" | |
| 24 #include "test/cctest/compiler/codegen-tester.h" | |
| 25 #include "test/cctest/compiler/graph-builder-tester.h" | |
| 26 | |
| 27 // TODO(titzer): pull WASM_64 up to a common header. | |
| 28 #if !V8_TARGET_ARCH_32_BIT || V8_TARGET_ARCH_X64 | |
| 29 #define WASM_64 1 | |
| 30 #else | |
| 31 #define WASM_64 0 | |
| 32 #endif | |
| 33 | |
| 34 // TODO(titzer): check traps more robustly in tests. | |
| 35 // Currently, in tests, we just return 0xdeadbeef from the function in which | |
| 36 // the trap occurs if the runtime context is not available to throw a JavaScript | |
| 37 // exception. | |
| 38 #define CHECK_TRAP32(x) \ | |
| 39 CHECK_EQ(0xdeadbeef, (bit_cast<uint32_t>(x)) & 0xFFFFFFFF) | |
| 40 #define CHECK_TRAP64(x) \ | |
| 41 CHECK_EQ(0xdeadbeefdeadbeef, (bit_cast<uint64_t>(x)) & 0xFFFFFFFFFFFFFFFF) | |
| 42 #define CHECK_TRAP(x) CHECK_TRAP32(x) | |
| 43 | |
| 44 namespace { | |
| 45 using namespace v8::base; | |
| 46 using namespace v8::internal; | |
| 47 using namespace v8::internal::compiler; | |
| 48 using namespace v8::internal::wasm; | |
| 49 | |
| 50 inline void init_env(FunctionEnv* env, FunctionSig* sig) { | |
| 51 env->module = nullptr; | |
| 52 env->sig = sig; | |
| 53 env->local_int32_count = 0; | |
| 54 env->local_int64_count = 0; | |
| 55 env->local_float32_count = 0; | |
| 56 env->local_float64_count = 0; | |
| 57 env->SumLocals(); | |
| 58 } | |
| 59 | |
| 60 const uint32_t kMaxGlobalsSize = 128; | |
| 61 | |
| 62 // A helper for module environments that adds the ability to allocate memory | |
| 63 // and global variables. | |
| 64 class TestingModule : public ModuleEnv { | |
| 65 public: | |
| 66 TestingModule() : mem_size(0), global_offset(0) { | |
| 67 globals_area = 0; | |
| 68 mem_start = 0; | |
| 69 mem_end = 0; | |
| 70 module = nullptr; | |
| 71 linker = nullptr; | |
| 72 function_code = nullptr; | |
| 73 asm_js = false; | |
| 74 memset(global_data, 0, sizeof(global_data)); | |
| 75 } | |
| 76 | |
| 77 ~TestingModule() { | |
| 78 if (mem_start) { | |
| 79 free(raw_mem_start<byte>()); | |
| 80 } | |
| 81 if (function_code) delete function_code; | |
| 82 if (module) delete module; | |
| 83 } | |
| 84 | |
| 85 byte* AddMemory(size_t size) { | |
| 86 CHECK_EQ(0, mem_start); | |
| 87 CHECK_EQ(0, mem_size); | |
| 88 mem_start = reinterpret_cast<uintptr_t>(malloc(size)); | |
| 89 CHECK(mem_start); | |
| 90 byte* raw = raw_mem_start<byte>(); | |
| 91 memset(raw, 0, size); | |
| 92 mem_end = mem_start + size; | |
| 93 mem_size = size; | |
| 94 return raw_mem_start<byte>(); | |
| 95 } | |
| 96 | |
| 97 template <typename T> | |
| 98 T* AddMemoryElems(size_t count) { | |
| 99 AddMemory(count * sizeof(T)); | |
| 100 return raw_mem_start<T>(); | |
| 101 } | |
| 102 | |
| 103 template <typename T> | |
| 104 T* AddGlobal(MachineType mem_type) { | |
| 105 WasmGlobal* global = AddGlobal(mem_type); | |
| 106 return reinterpret_cast<T*>(globals_area + global->offset); | |
| 107 } | |
| 108 | |
| 109 byte AddSignature(FunctionSig* sig) { | |
| 110 AllocModule(); | |
| 111 if (!module->signatures) { | |
| 112 module->signatures = new std::vector<FunctionSig*>(); | |
| 113 } | |
| 114 module->signatures->push_back(sig); | |
| 115 size_t size = module->signatures->size(); | |
| 116 CHECK(size < 127); | |
| 117 return static_cast<byte>(size - 1); | |
| 118 } | |
| 119 | |
| 120 template <typename T> | |
| 121 T* raw_mem_start() { | |
| 122 DCHECK(mem_start); | |
| 123 return reinterpret_cast<T*>(mem_start); | |
| 124 } | |
| 125 | |
| 126 template <typename T> | |
| 127 T* raw_mem_end() { | |
| 128 DCHECK(mem_end); | |
| 129 return reinterpret_cast<T*>(mem_end); | |
| 130 } | |
| 131 | |
| 132 template <typename T> | |
| 133 T raw_mem_at(int i) { | |
| 134 DCHECK(mem_start); | |
| 135 return reinterpret_cast<T*>(mem_start)[i]; | |
| 136 } | |
| 137 | |
| 138 template <typename T> | |
| 139 T raw_val_at(int i) { | |
| 140 T val; | |
| 141 memcpy(&val, reinterpret_cast<void*>(mem_start + i), sizeof(T)); | |
| 142 return val; | |
| 143 } | |
| 144 | |
| 145 // Zero-initialize the memory. | |
| 146 void BlankMemory() { | |
| 147 byte* raw = raw_mem_start<byte>(); | |
| 148 memset(raw, 0, mem_size); | |
| 149 } | |
| 150 | |
| 151 // Pseudo-randomly intialize the memory. | |
| 152 void RandomizeMemory(unsigned int seed = 88) { | |
| 153 byte* raw = raw_mem_start<byte>(); | |
| 154 byte* end = raw_mem_end<byte>(); | |
| 155 v8::base::RandomNumberGenerator rng; | |
| 156 rng.SetSeed(seed); | |
| 157 rng.NextBytes(raw, end - raw); | |
| 158 } | |
| 159 | |
| 160 WasmFunction* AddFunction(FunctionSig* sig, Handle<Code> code) { | |
| 161 AllocModule(); | |
| 162 if (module->functions == nullptr) { | |
| 163 module->functions = new std::vector<WasmFunction>(); | |
| 164 function_code = new std::vector<Handle<Code>>(); | |
| 165 } | |
| 166 module->functions->push_back({sig, 0, 0, 0, 0, 0, 0, 0, false, false}); | |
| 167 function_code->push_back(code); | |
| 168 return &module->functions->back(); | |
| 169 } | |
| 170 | |
| 171 private: | |
| 172 size_t mem_size; | |
| 173 uint32_t global_offset; | |
| 174 byte global_data[kMaxGlobalsSize]; | |
| 175 | |
| 176 WasmGlobal* AddGlobal(MachineType mem_type) { | |
| 177 AllocModule(); | |
| 178 if (globals_area == 0) { | |
| 179 globals_area = reinterpret_cast<uintptr_t>(global_data); | |
| 180 module->globals = new std::vector<WasmGlobal>(); | |
| 181 } | |
| 182 byte size = WasmOpcodes::MemSize(mem_type); | |
| 183 global_offset = (global_offset + size - 1) & ~(size - 1); // align | |
| 184 module->globals->push_back({0, mem_type, global_offset, false}); | |
| 185 global_offset += size; | |
| 186 // limit number of globals. | |
| 187 CHECK_LT(global_offset, kMaxGlobalsSize); | |
| 188 return &module->globals->back(); | |
| 189 } | |
| 190 void AllocModule() { | |
| 191 if (module == nullptr) { | |
| 192 module = new WasmModule(); | |
| 193 module->shared_isolate = CcTest::InitIsolateOnce(); | |
| 194 module->globals = nullptr; | |
| 195 module->functions = nullptr; | |
| 196 module->data_segments = nullptr; | |
| 197 } | |
| 198 } | |
| 199 }; | |
| 200 | |
| 201 | |
| 202 inline void TestBuildingGraph(Zone* zone, JSGraph* jsgraph, FunctionEnv* env, | |
| 203 const byte* start, const byte* end) { | |
| 204 compiler::WasmGraphBuilder builder(zone, jsgraph, env->sig); | |
| 205 TreeResult result = BuildTFGraph(&builder, env, start, end); | |
| 206 if (result.failed()) { | |
| 207 ptrdiff_t pc = result.error_pc - result.start; | |
| 208 ptrdiff_t pt = result.error_pt - result.start; | |
| 209 std::ostringstream str; | |
| 210 str << "Verification failed: " << result.error_code << " pc = +" << pc; | |
| 211 if (result.error_pt) str << ", pt = +" << pt; | |
| 212 str << ", msg = " << result.error_msg.get(); | |
| 213 FATAL(str.str().c_str()); | |
| 214 } | |
| 215 if (FLAG_trace_turbo_graph) { | |
| 216 OFStream os(stdout); | |
| 217 os << AsRPO(*jsgraph->graph()); | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 | |
| 222 // A helper for compiling functions that are only internally callable WASM code. | |
| 223 class WasmFunctionCompiler : public HandleAndZoneScope, | |
| 224 private GraphAndBuilders { | |
| 225 public: | |
| 226 explicit WasmFunctionCompiler(FunctionSig* sig, ModuleEnv* module = nullptr) | |
| 227 : GraphAndBuilders(main_zone()), | |
| 228 jsgraph(this->isolate(), this->graph(), this->common(), nullptr, | |
| 229 nullptr, this->machine()), | |
| 230 descriptor_(nullptr) { | |
| 231 init_env(&env, sig); | |
| 232 env.module = module; | |
| 233 } | |
| 234 | |
| 235 JSGraph jsgraph; | |
| 236 FunctionEnv env; | |
| 237 // The call descriptor is initialized when the function is compiled. | |
| 238 CallDescriptor* descriptor_; | |
| 239 | |
| 240 Isolate* isolate() { return main_isolate(); } | |
| 241 Graph* graph() const { return main_graph_; } | |
| 242 Zone* zone() const { return graph()->zone(); } | |
| 243 CommonOperatorBuilder* common() { return &main_common_; } | |
| 244 MachineOperatorBuilder* machine() { return &main_machine_; } | |
| 245 CallDescriptor* descriptor() { return descriptor_; } | |
| 246 | |
| 247 void Build(const byte* start, const byte* end) { | |
| 248 TestBuildingGraph(main_zone(), &jsgraph, &env, start, end); | |
| 249 } | |
| 250 | |
| 251 byte AllocateLocal(LocalType type) { | |
| 252 int result = static_cast<int>(env.total_locals); | |
| 253 env.AddLocals(type, 1); | |
| 254 byte b = static_cast<byte>(result); | |
| 255 CHECK_EQ(result, b); | |
| 256 return b; | |
| 257 } | |
| 258 | |
| 259 Handle<Code> Compile(ModuleEnv* module) { | |
| 260 descriptor_ = module->GetWasmCallDescriptor(this->zone(), env.sig); | |
| 261 CompilationInfo info("wasm compile", this->isolate(), this->zone()); | |
| 262 Handle<Code> result = | |
| 263 Pipeline::GenerateCodeForTesting(&info, descriptor_, this->graph()); | |
| 264 #ifdef ENABLE_DISASSEMBLER | |
| 265 if (!result.is_null() && FLAG_print_opt_code) { | |
| 266 OFStream os(stdout); | |
| 267 result->Disassemble("wasm code", os); | |
| 268 } | |
| 269 #endif | |
| 270 | |
| 271 return result; | |
| 272 } | |
| 273 | |
| 274 uint32_t CompileAndAdd(TestingModule* module) { | |
| 275 uint32_t index = 0; | |
| 276 if (module->module && module->module->functions) { | |
| 277 index = static_cast<uint32_t>(module->module->functions->size()); | |
| 278 } | |
| 279 module->AddFunction(env.sig, Compile(module)); | |
| 280 return index; | |
| 281 } | |
| 282 }; | |
| 283 | |
| 284 | |
| 285 // A helper class to build graphs from Wasm bytecode, generate machine | |
| 286 // code, and run that code. | |
| 287 template <typename ReturnType> | |
| 288 class WasmRunner { | |
| 289 public: | |
| 290 WasmRunner(MachineType p0 = MachineType::None(), | |
| 291 MachineType p1 = MachineType::None(), | |
| 292 MachineType p2 = MachineType::None(), | |
| 293 MachineType p3 = MachineType::None()) | |
| 294 : signature_(MachineTypeForC<ReturnType>() == MachineType::None() ? 0 : 1, | |
| 295 GetParameterCount(p0, p1, p2, p3), storage_), | |
| 296 compiler_(&signature_), | |
| 297 call_wrapper_(p0, p1, p2, p3), | |
| 298 compilation_done_(false) { | |
| 299 int index = 0; | |
| 300 MachineType ret = MachineTypeForC<ReturnType>(); | |
| 301 if (ret != MachineType::None()) { | |
| 302 storage_[index++] = WasmOpcodes::LocalTypeFor(ret); | |
| 303 } | |
| 304 if (p0 != MachineType::None()) | |
| 305 storage_[index++] = WasmOpcodes::LocalTypeFor(p0); | |
| 306 if (p1 != MachineType::None()) | |
| 307 storage_[index++] = WasmOpcodes::LocalTypeFor(p1); | |
| 308 if (p2 != MachineType::None()) | |
| 309 storage_[index++] = WasmOpcodes::LocalTypeFor(p2); | |
| 310 if (p3 != MachineType::None()) | |
| 311 storage_[index++] = WasmOpcodes::LocalTypeFor(p3); | |
| 312 } | |
| 313 | |
| 314 | |
| 315 FunctionEnv* env() { return &compiler_.env; } | |
| 316 | |
| 317 | |
| 318 // Builds a graph from the given Wasm code, and generates the machine | |
| 319 // code and call wrapper for that graph. This method must not be called | |
| 320 // more than once. | |
| 321 void Build(const byte* start, const byte* end) { | |
| 322 DCHECK(!compilation_done_); | |
| 323 compilation_done_ = true; | |
| 324 // Build the TF graph. | |
| 325 compiler_.Build(start, end); | |
| 326 // Generate code. | |
| 327 Handle<Code> code = compiler_.Compile(env()->module); | |
| 328 | |
| 329 // Construct the call wrapper. | |
| 330 Node* inputs[5]; | |
| 331 int input_count = 0; | |
| 332 inputs[input_count++] = call_wrapper_.HeapConstant(code); | |
| 333 for (size_t i = 0; i < signature_.parameter_count(); i++) { | |
| 334 inputs[input_count++] = call_wrapper_.Parameter(i); | |
| 335 } | |
| 336 | |
| 337 call_wrapper_.Return(call_wrapper_.AddNode( | |
| 338 call_wrapper_.common()->Call(compiler_.descriptor()), input_count, | |
| 339 inputs)); | |
| 340 } | |
| 341 | |
| 342 ReturnType Call() { return call_wrapper_.Call(); } | |
| 343 | |
| 344 template <typename P0> | |
| 345 ReturnType Call(P0 p0) { | |
| 346 return call_wrapper_.Call(p0); | |
| 347 } | |
| 348 | |
| 349 template <typename P0, typename P1> | |
| 350 ReturnType Call(P0 p0, P1 p1) { | |
| 351 return call_wrapper_.Call(p0, p1); | |
| 352 } | |
| 353 | |
| 354 template <typename P0, typename P1, typename P2> | |
| 355 ReturnType Call(P0 p0, P1 p1, P2 p2) { | |
| 356 return call_wrapper_.Call(p0, p1, p2); | |
| 357 } | |
| 358 | |
| 359 template <typename P0, typename P1, typename P2, typename P3> | |
| 360 ReturnType Call(P0 p0, P1 p1, P2 p2, P3 p3) { | |
| 361 return call_wrapper_.Call(p0, p1, p2, p3); | |
| 362 } | |
| 363 | |
| 364 byte AllocateLocal(LocalType type) { | |
| 365 int result = static_cast<int>(env()->total_locals); | |
| 366 env()->AddLocals(type, 1); | |
| 367 byte b = static_cast<byte>(result); | |
| 368 CHECK_EQ(result, b); | |
| 369 return b; | |
| 370 } | |
| 371 | |
| 372 private: | |
| 373 LocalType storage_[5]; | |
| 374 FunctionSig signature_; | |
| 375 WasmFunctionCompiler compiler_; | |
| 376 BufferedRawMachineAssemblerTester<ReturnType> call_wrapper_; | |
| 377 bool compilation_done_; | |
| 378 | |
| 379 static size_t GetParameterCount(MachineType p0, MachineType p1, | |
| 380 MachineType p2, MachineType p3) { | |
| 381 if (p0 == MachineType::None()) return 0; | |
| 382 if (p1 == MachineType::None()) return 1; | |
| 383 if (p2 == MachineType::None()) return 2; | |
| 384 if (p3 == MachineType::None()) return 3; | |
| 385 return 4; | |
| 386 } | |
| 387 }; | |
| 388 | |
| 389 } // namespace | |
| 390 | |
| 391 #endif | |
| OLD | NEW |