// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/wasm/function-body-decoder.h" #include "src/flags/flags.h" #include "src/handles/handles.h" #include "src/objects/objects-inl.h" #include "src/utils/ostreams.h" #include "src/wasm/decoder.h" #include "src/wasm/function-body-decoder-impl.h" #include "src/wasm/wasm-limits.h" #include "src/wasm/wasm-linkage.h" #include "src/wasm/wasm-module.h" #include "src/wasm/wasm-opcodes.h" namespace v8 { namespace internal { namespace wasm { bool DecodeLocalDecls(const WasmFeatures& enabled, BodyLocalDecls* decls, const byte* start, const byte* end) { Decoder decoder(start, end); if (WasmDecoder::DecodeLocals(enabled, &decoder, nullptr, &decls->type_list)) { DCHECK(decoder.ok()); decls->encoded_size = decoder.pc_offset(); return true; } return false; } BytecodeIterator::BytecodeIterator(const byte* start, const byte* end, BodyLocalDecls* decls) : Decoder(start, end) { if (decls != nullptr) { if (DecodeLocalDecls(kAllWasmFeatures, decls, start, end)) { pc_ += decls->encoded_size; if (pc_ > end_) pc_ = end_; } } } DecodeResult VerifyWasmCode(AccountingAllocator* allocator, const WasmFeatures& enabled, const WasmModule* module, WasmFeatures* detected, const FunctionBody& body) { Zone zone(allocator, ZONE_NAME); WasmFullDecoder decoder( &zone, module, enabled, detected, body); decoder.Decode(); return decoder.toResult(nullptr); } unsigned OpcodeLength(const byte* pc, const byte* end) { Decoder decoder(pc, end); return WasmDecoder::OpcodeLength(&decoder, pc); } std::pair StackEffect(const WasmModule* module, FunctionSig* sig, const byte* pc, const byte* end) { WasmFeatures unused_detected_features; WasmDecoder decoder( module, kAllWasmFeatures, &unused_detected_features, sig, pc, end); return decoder.StackEffect(pc); } void PrintRawWasmCode(const byte* start, const byte* end) { AccountingAllocator allocator; PrintRawWasmCode(&allocator, FunctionBody{nullptr, 0, start, end}, nullptr, kPrintLocals); } namespace { const char* RawOpcodeName(WasmOpcode opcode) { switch (opcode) { #define DECLARE_NAME_CASE(name, opcode, sig) \ case kExpr##name: \ return "kExpr" #name; FOREACH_OPCODE(DECLARE_NAME_CASE) #undef DECLARE_NAME_CASE default: break; } return "Unknown"; } } // namespace bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body, const WasmModule* module, PrintLocals print_locals) { StdoutStream os; return PrintRawWasmCode(allocator, body, module, print_locals, os); } bool PrintRawWasmCode(AccountingAllocator* allocator, const FunctionBody& body, const WasmModule* module, PrintLocals print_locals, std::ostream& os, std::vector* line_numbers) { Zone zone(allocator, ZONE_NAME); WasmFeatures unused_detected_features; WasmDecoder decoder(module, kAllWasmFeatures, &unused_detected_features, body.sig, body.start, body.end); int line_nr = 0; constexpr int kNoByteCode = -1; // Print the function signature. if (body.sig) { os << "// signature: " << *body.sig << std::endl; if (line_numbers) line_numbers->push_back(kNoByteCode); ++line_nr; } // Print the local declarations. BodyLocalDecls decls(&zone); BytecodeIterator i(body.start, body.end, &decls); if (body.start != i.pc() && print_locals == kPrintLocals) { os << "// locals:"; if (!decls.type_list.empty()) { ValueType type = decls.type_list[0]; uint32_t count = 0; for (size_t pos = 0; pos < decls.type_list.size(); ++pos) { if (decls.type_list[pos] == type) { ++count; } else { os << " " << count << " " << ValueTypes::TypeName(type); type = decls.type_list[pos]; count = 1; } } os << " " << count << " " << ValueTypes::TypeName(type); } os << std::endl; if (line_numbers) line_numbers->push_back(kNoByteCode); ++line_nr; for (const byte* locals = body.start; locals < i.pc(); locals++) { os << (locals == body.start ? "0x" : " 0x") << AsHex(*locals, 2) << ","; } os << std::endl; if (line_numbers) line_numbers->push_back(kNoByteCode); ++line_nr; } os << "// body: " << std::endl; if (line_numbers) line_numbers->push_back(kNoByteCode); ++line_nr; unsigned control_depth = 0; for (; i.has_next(); i.next()) { unsigned length = WasmDecoder::OpcodeLength(&decoder, i.pc()); unsigned offset = 1; WasmOpcode opcode = i.current(); if (WasmOpcodes::IsPrefixOpcode(opcode)) { opcode = i.prefixed_opcode(); offset = 2; } if (line_numbers) line_numbers->push_back(i.position()); if (opcode == kExprElse || opcode == kExprCatch) { control_depth--; } int num_whitespaces = control_depth < 32 ? 2 * control_depth : 64; // 64 whitespaces const char* padding = " "; os.write(padding, num_whitespaces); os << RawOpcodeName(opcode) << ","; if (opcode == kExprLoop || opcode == kExprIf || opcode == kExprBlock || opcode == kExprTry) { DCHECK_EQ(2, length); switch (i.pc()[1]) { #define CASE_LOCAL_TYPE(local_name, type_name) \ case kLocal##local_name: \ os << " kWasm" #type_name ","; \ break; CASE_LOCAL_TYPE(I32, I32) CASE_LOCAL_TYPE(I64, I64) CASE_LOCAL_TYPE(F32, F32) CASE_LOCAL_TYPE(F64, F64) CASE_LOCAL_TYPE(S128, S128) CASE_LOCAL_TYPE(Void, Stmt) default: os << " 0x" << AsHex(i.pc()[1], 2) << ","; break; } #undef CASE_LOCAL_TYPE } else { for (unsigned j = offset; j < length; ++j) { os << " 0x" << AsHex(i.pc()[j], 2) << ","; } } switch (opcode) { case kExprElse: case kExprCatch: os << " // @" << i.pc_offset(); control_depth++; break; case kExprLoop: case kExprIf: case kExprBlock: case kExprTry: { BlockTypeImmediate imm(kAllWasmFeatures, &i, i.pc()); os << " // @" << i.pc_offset(); if (decoder.Complete(imm)) { for (uint32_t i = 0; i < imm.out_arity(); i++) { os << " " << ValueTypes::TypeName(imm.out_type(i)); } } control_depth++; break; } case kExprEnd: os << " // @" << i.pc_offset(); control_depth--; break; case kExprBr: { BranchDepthImmediate imm(&i, i.pc()); os << " // depth=" << imm.depth; break; } case kExprBrIf: { BranchDepthImmediate imm(&i, i.pc()); os << " // depth=" << imm.depth; break; } case kExprBrTable: { BranchTableImmediate imm(&i, i.pc()); os << " // entries=" << imm.table_count; break; } case kExprCallIndirect: { CallIndirectImmediate imm(kAllWasmFeatures, &i, i.pc()); os << " // sig #" << imm.sig_index; if (decoder.Complete(i.pc(), imm)) { os << ": " << *imm.sig; } break; } case kExprCallFunction: { CallFunctionImmediate imm(&i, i.pc()); os << " // function #" << imm.index; if (decoder.Complete(i.pc(), imm)) { os << ": " << *imm.sig; } break; } default: break; } os << std::endl; ++line_nr; } DCHECK(!line_numbers || line_numbers->size() == static_cast(line_nr)); return decoder.ok(); } BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, size_t num_locals, const byte* start, const byte* end) { Decoder decoder(start, end); return WasmDecoder::AnalyzeLoopAssignment( &decoder, start, static_cast(num_locals), zone); } } // namespace wasm } // namespace internal } // namespace v8