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 #include "src/wasm/wasm-text.h" |
| 6 |
| 7 #include "src/ostreams.h" |
| 8 #include "src/vector.h" |
| 9 #include "src/wasm/ast-decoder.h" |
| 10 #include "src/wasm/wasm-module.h" |
| 11 #include "src/wasm/wasm-opcodes.h" |
| 12 #include "src/zone/zone.h" |
| 13 |
| 14 using namespace v8::internal; |
| 15 using namespace v8::internal::wasm; |
| 16 |
| 17 namespace { |
| 18 const char *GetOpName(WasmOpcode opcode) { |
| 19 #define CASE_OP(name, str) \ |
| 20 case kExpr##name: \ |
| 21 return str; |
| 22 #define CASE_I32_OP(name, str) CASE_OP(I32##name, "i32." str) |
| 23 #define CASE_I64_OP(name, str) CASE_OP(I64##name, "i64." str) |
| 24 #define CASE_F32_OP(name, str) CASE_OP(F32##name, "f32." str) |
| 25 #define CASE_F64_OP(name, str) CASE_OP(F64##name, "f64." str) |
| 26 #define CASE_INT_OP(name, str) CASE_I32_OP(name, str) CASE_I64_OP(name, str) |
| 27 #define CASE_FLOAT_OP(name, str) CASE_F32_OP(name, str) CASE_F64_OP(name, str) |
| 28 #define CASE_ALL_OP(name, str) CASE_FLOAT_OP(name, str) CASE_INT_OP(name, str) |
| 29 #define CASE_SIGN_OP(TYPE, name, str) \ |
| 30 CASE_##TYPE##_OP(name##S, str "_s") CASE_##TYPE##_OP(name##U, str "_u") |
| 31 #define CASE_ALL_SIGN_OP(name, str) \ |
| 32 CASE_FLOAT_OP(name, str) CASE_SIGN_OP(INT, name, str) |
| 33 #define CASE_CONVERT_OP(name, RES, SRC, src_suffix, str) \ |
| 34 CASE_##RES##_OP(U##name##SRC, str "_u/" src_suffix) \ |
| 35 CASE_##RES##_OP(S##name##SRC, str "_s/" src_suffix) |
| 36 |
| 37 switch (opcode) { |
| 38 CASE_INT_OP(Eqz, "eqz") |
| 39 CASE_ALL_OP(Eq, "eq") |
| 40 CASE_ALL_OP(Ne, "ne") |
| 41 CASE_ALL_OP(Add, "add") |
| 42 CASE_ALL_OP(Sub, "sub") |
| 43 CASE_ALL_OP(Mul, "mul") |
| 44 CASE_ALL_SIGN_OP(Lt, "lt") |
| 45 CASE_ALL_SIGN_OP(Gt, "gt") |
| 46 CASE_ALL_SIGN_OP(Le, "le") |
| 47 CASE_ALL_SIGN_OP(Ge, "ge") |
| 48 CASE_INT_OP(Clz, "clz") |
| 49 CASE_INT_OP(Ctz, "ctz") |
| 50 CASE_INT_OP(Popcnt, "popcnt") |
| 51 CASE_ALL_SIGN_OP(Div, "div") |
| 52 CASE_SIGN_OP(INT, Rem, "rem") |
| 53 CASE_INT_OP(And, "and") |
| 54 CASE_INT_OP(Ior, "or") |
| 55 CASE_INT_OP(Xor, "xor") |
| 56 CASE_INT_OP(Shl, "shl") |
| 57 CASE_SIGN_OP(INT, Shr, "shr") |
| 58 CASE_INT_OP(Rol, "rol") |
| 59 CASE_INT_OP(Ror, "ror") |
| 60 CASE_FLOAT_OP(Abs, "abs") |
| 61 CASE_FLOAT_OP(Neg, "neg") |
| 62 CASE_FLOAT_OP(Ceil, "ceil") |
| 63 CASE_FLOAT_OP(Floor, "floor") |
| 64 CASE_FLOAT_OP(Trunc, "trunc") |
| 65 CASE_FLOAT_OP(NearestInt, "nearest") |
| 66 CASE_FLOAT_OP(Sqrt, "sqrt") |
| 67 CASE_FLOAT_OP(Min, "min") |
| 68 CASE_FLOAT_OP(Max, "max") |
| 69 CASE_FLOAT_OP(CopySign, "copysign") |
| 70 CASE_I32_OP(ConvertI64, "wrap/i64") |
| 71 CASE_CONVERT_OP(Convert, INT, F32, "f32", "trunc") |
| 72 CASE_CONVERT_OP(Convert, INT, F64, "f64", "trunc") |
| 73 CASE_CONVERT_OP(Convert, I64, I32, "i32", "extend") |
| 74 CASE_CONVERT_OP(Convert, F32, I32, "i32", "convert") |
| 75 CASE_CONVERT_OP(Convert, F32, I64, "i64", "convert") |
| 76 CASE_F32_OP(ConvertF64, "demote/f64") |
| 77 CASE_CONVERT_OP(Convert, F64, I32, "i32", "convert") |
| 78 CASE_CONVERT_OP(Convert, F64, I64, "i64", "convert") |
| 79 CASE_F64_OP(ConvertF32, "promote/f32") |
| 80 CASE_I32_OP(ReinterpretF32, "reinterpret/f32") |
| 81 CASE_I64_OP(ReinterpretF64, "reinterpret/f64") |
| 82 CASE_F32_OP(ReinterpretI32, "reinterpret/i32") |
| 83 CASE_F64_OP(ReinterpretI64, "reinterpret/i64") |
| 84 CASE_OP(Unreachable, "unreachable") |
| 85 CASE_OP(Nop, "nop") |
| 86 CASE_OP(Return, "return") |
| 87 CASE_OP(MemorySize, "current_memory") |
| 88 CASE_OP(GrowMemory, "grow_memory") |
| 89 CASE_OP(Loop, "loop") |
| 90 CASE_OP(If, "if") |
| 91 CASE_OP(Block, "block") |
| 92 CASE_OP(Try, "try") |
| 93 CASE_OP(Throw, "throw") |
| 94 CASE_OP(Catch, "catch") |
| 95 CASE_OP(Drop, "drop") |
| 96 CASE_ALL_OP(LoadMem, "load") |
| 97 CASE_SIGN_OP(INT, LoadMem8, "load8") |
| 98 CASE_SIGN_OP(INT, LoadMem16, "load16") |
| 99 CASE_SIGN_OP(I64, LoadMem32, "load32") |
| 100 CASE_ALL_OP(StoreMem, "store") |
| 101 CASE_INT_OP(StoreMem8, "store8") |
| 102 CASE_INT_OP(StoreMem16, "store16") |
| 103 CASE_I64_OP(StoreMem32, "store32") |
| 104 CASE_OP(SetLocal, "set_local") |
| 105 CASE_OP(GetLocal, "get_local") |
| 106 CASE_OP(TeeLocal, "tee_local") |
| 107 CASE_OP(GetGlobal, "get_global") |
| 108 CASE_OP(SetGlobal, "set_global") |
| 109 CASE_OP(Br, "br") |
| 110 CASE_OP(BrIf, "br_if") |
| 111 default: |
| 112 UNREACHABLE(); |
| 113 return ""; |
| 114 } |
| 115 } |
| 116 |
| 117 bool IsValidFunctionName(const Vector<const char> &name) { |
| 118 if (name.is_empty()) return false; |
| 119 const char *special_chars = "_.+-*/\\^~=<>!?@#$%&|:'`"; |
| 120 for (char c : name) { |
| 121 bool valid_char = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || |
| 122 (c >= 'A' && c <= 'Z') || strchr(special_chars, c); |
| 123 if (!valid_char) return false; |
| 124 } |
| 125 return true; |
| 126 } |
| 127 |
| 128 } // namespace |
| 129 |
| 130 void wasm::PrintWasmText( |
| 131 const WasmModule *module, uint32_t func_index, std::ostream &os, |
| 132 std::vector<std::tuple<uint32_t, int, int>> *offset_table) { |
| 133 DCHECK_NOT_NULL(module); |
| 134 DCHECK_GT(module->functions.size(), func_index); |
| 135 const WasmFunction *fun = &module->functions[func_index]; |
| 136 |
| 137 AccountingAllocator allocator; |
| 138 Zone zone(&allocator, ZONE_NAME); |
| 139 int line_nr = 0; |
| 140 int control_depth = 0; |
| 141 |
| 142 // Print the function signature. |
| 143 os << "func"; |
| 144 Vector<const char> fun_name( |
| 145 reinterpret_cast<const char *>(module->module_start + fun->name_offset), |
| 146 fun->name_length); |
| 147 if (IsValidFunctionName(fun_name)) { |
| 148 os << " $"; |
| 149 os.write(fun_name.start(), fun_name.length()); |
| 150 } |
| 151 size_t param_count = fun->sig->parameter_count(); |
| 152 if (param_count) { |
| 153 os << " (param"; |
| 154 for (size_t i = 0; i < param_count; ++i) |
| 155 os << ' ' << WasmOpcodes::TypeName(fun->sig->GetParam(i)); |
| 156 os << ')'; |
| 157 } |
| 158 size_t return_count = fun->sig->return_count(); |
| 159 if (return_count) { |
| 160 os << " (result"; |
| 161 for (size_t i = 0; i < return_count; ++i) |
| 162 os << ' ' << WasmOpcodes::TypeName(fun->sig->GetReturn(i)); |
| 163 os << ')'; |
| 164 } |
| 165 os << "\n"; |
| 166 ++line_nr; |
| 167 |
| 168 // Print the local declarations. |
| 169 AstLocalDecls decls(&zone); |
| 170 const byte *code_start = module->module_start + fun->code_start_offset; |
| 171 const byte *code_end = module->module_start + fun->code_end_offset; |
| 172 BytecodeIterator i(code_start, code_end, &decls); |
| 173 DCHECK_LT(code_start, i.pc()); |
| 174 if (!decls.local_types.empty()) { |
| 175 os << "(local"; |
| 176 for (auto p : decls.local_types) { |
| 177 for (unsigned i = 0; i < p.second; ++i) |
| 178 os << ' ' << WasmOpcodes::TypeName(p.first); |
| 179 } |
| 180 os << ")\n"; |
| 181 ++line_nr; |
| 182 } |
| 183 |
| 184 for (; i.has_next(); i.next()) { |
| 185 WasmOpcode opcode = i.current(); |
| 186 if (opcode == kExprElse || opcode == kExprEnd) --control_depth; |
| 187 |
| 188 DCHECK_LE(0, control_depth); |
| 189 const int kMaxIndentation = 64; |
| 190 int indentation = std::min(kMaxIndentation, 2 * control_depth); |
| 191 if (offset_table) { |
| 192 offset_table->push_back( |
| 193 std::make_tuple(i.pc_offset(), line_nr, indentation)); |
| 194 } |
| 195 |
| 196 // 64 whitespaces |
| 197 const char padding[kMaxIndentation + 1] = |
| 198 " "; |
| 199 os.write(padding, indentation); |
| 200 |
| 201 switch (opcode) { |
| 202 case kExprLoop: |
| 203 case kExprIf: |
| 204 case kExprBlock: |
| 205 case kExprTry: { |
| 206 BlockTypeOperand operand(&i, i.pc()); |
| 207 os << GetOpName(opcode); |
| 208 for (unsigned i = 0; i < operand.arity; i++) { |
| 209 os << " " << WasmOpcodes::TypeName(operand.read_entry(i)); |
| 210 } |
| 211 control_depth++; |
| 212 break; |
| 213 } |
| 214 case kExprBr: |
| 215 case kExprBrIf: { |
| 216 BreakDepthOperand operand(&i, i.pc()); |
| 217 os << GetOpName(opcode) << ' ' << operand.depth; |
| 218 break; |
| 219 } |
| 220 case kExprElse: |
| 221 os << "else"; |
| 222 control_depth++; |
| 223 break; |
| 224 case kExprEnd: |
| 225 os << "end"; |
| 226 break; |
| 227 case kExprBrTable: { |
| 228 BranchTableOperand operand(&i, i.pc()); |
| 229 BranchTableIterator iterator(&i, operand); |
| 230 os << "br_table"; |
| 231 while (iterator.has_next()) os << ' ' << iterator.next(); |
| 232 break; |
| 233 } |
| 234 case kExprCallIndirect: { |
| 235 CallIndirectOperand operand(&i, i.pc()); |
| 236 DCHECK_EQ(0U, operand.table_index); |
| 237 os << "call_indirect " << operand.index; |
| 238 break; |
| 239 } |
| 240 case kExprCallFunction: { |
| 241 CallFunctionOperand operand(&i, i.pc()); |
| 242 os << "call " << operand.index; |
| 243 break; |
| 244 } |
| 245 case kExprGetLocal: |
| 246 case kExprSetLocal: |
| 247 case kExprTeeLocal: |
| 248 case kExprCatch: { |
| 249 LocalIndexOperand operand(&i, i.pc()); |
| 250 os << GetOpName(opcode) << ' ' << operand.index; |
| 251 break; |
| 252 } |
| 253 case kExprGetGlobal: |
| 254 case kExprSetGlobal: { |
| 255 GlobalIndexOperand operand(&i, i.pc()); |
| 256 os << GetOpName(opcode) << ' ' << operand.index; |
| 257 break; |
| 258 } |
| 259 #define CASE_CONST(type, str, cast_type) \ |
| 260 case kExpr##type##Const: { \ |
| 261 Imm##type##Operand operand(&i, i.pc()); \ |
| 262 os << #str ".const " << static_cast<cast_type>(operand.value); \ |
| 263 break; \ |
| 264 } |
| 265 CASE_CONST(I8, i8, int32_t) |
| 266 CASE_CONST(I32, i32, int32_t) |
| 267 CASE_CONST(I64, i64, int64_t) |
| 268 CASE_CONST(F32, f32, float) |
| 269 CASE_CONST(F64, f64, double) |
| 270 |
| 271 #define CASE_OPCODE(opcode, _, __) case kExpr##opcode: |
| 272 FOREACH_LOAD_MEM_OPCODE(CASE_OPCODE) |
| 273 FOREACH_STORE_MEM_OPCODE(CASE_OPCODE) { |
| 274 MemoryAccessOperand operand(&i, i.pc(), kMaxUInt32); |
| 275 os << GetOpName(opcode) << " offset=" << operand.offset |
| 276 << " align=" << (1ULL << operand.alignment); |
| 277 break; |
| 278 } |
| 279 |
| 280 FOREACH_SIMPLE_OPCODE(CASE_OPCODE) |
| 281 case kExprUnreachable: |
| 282 case kExprNop: |
| 283 case kExprReturn: |
| 284 case kExprMemorySize: |
| 285 case kExprGrowMemory: |
| 286 case kExprDrop: |
| 287 case kExprThrow: |
| 288 os << GetOpName(opcode); |
| 289 break; |
| 290 |
| 291 // This group is just printed by their internal opcode name, as they |
| 292 // should never be shown to end-users. |
| 293 FOREACH_ASMJS_COMPAT_OPCODE(CASE_OPCODE) |
| 294 // TODO(wasm): Add correct printing for SIMD and atomic opcodes once |
| 295 // they are publicly available. |
| 296 FOREACH_SIMD_0_OPERAND_OPCODE(CASE_OPCODE) |
| 297 FOREACH_SIMD_1_OPERAND_OPCODE(CASE_OPCODE) |
| 298 FOREACH_ATOMIC_OPCODE(CASE_OPCODE) |
| 299 os << WasmOpcodes::OpcodeName(opcode); |
| 300 break; |
| 301 |
| 302 default: |
| 303 UNREACHABLE(); |
| 304 break; |
| 305 } |
| 306 os << '\n'; |
| 307 ++line_nr; |
| 308 } |
| 309 DCHECK_EQ(0, control_depth); |
| 310 DCHECK(i.ok()); |
| 311 } |
OLD | NEW |