[wasm][fuzzer] Complex init. expressions in JS testcase

We enable struct.new and array.init initializer expressions in the JS
testcase generated by --wasm-fuzzer-gen-test. We needed to make some
changes in the WasmInitExpr class, and to implement a new interface for
the WasmFullDecoder, which constructs a WasmInitExpr.
Changes:
- Make WasmInitExpr a ZoneObject. Use a pointer for its operands_ field.
  This is needed so WasmInitExpr is trivially copiable, and thus usable
  as a Value type in WasmFullDecoder.
- Implement a WasmFullDecoder interface in wasm-fuzzer-common that
  constructs a WasmInitExpr. Use it to decode initializers in the
  module generated by the fuzzer.
- Change AppendInitExpr to take a WasmInitExpr as argument.
- Fix an issue with printing of struct definitions.
- Change initializer expression used for structs to struct.new_with_rtt.
  This is consistent with the currently used structural types.

Bug: v8:11954
Change-Id: I65a87cc98701a54f32500be192b3b6eef2ff6c8c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3257712
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Thibaud Michaud <thibaudm@chromium.org>
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77730}
This commit is contained in:
Manos Koukoutos 2021-11-05 06:10:04 +00:00 committed by V8 LUCI CQ
parent d65a8d6cf5
commit 74d9a7642d
10 changed files with 304 additions and 123 deletions

View File

@ -239,7 +239,7 @@ void AsmJsParser::DeclareGlobal(VarInfo* info, bool mutable_variable,
WasmInitExpr init) {
info->kind = VarKind::kGlobal;
info->type = type;
info->index = module_builder_->AddGlobal(vtype, true, std::move(init));
info->index = module_builder_->AddGlobal(vtype, true, init);
info->mutable_variable = mutable_variable;
}

View File

@ -63,7 +63,10 @@ class InitExprInterface {
#define EMPTY_INTERFACE_FUNCTION(name, ...) \
V8_INLINE void name(FullDecoder* decoder, ##__VA_ARGS__) {}
INTERFACE_META_FUNCTIONS(EMPTY_INTERFACE_FUNCTION)
INTERFACE_NON_CONSTANT_FUNCTIONS(EMPTY_INTERFACE_FUNCTION)
#undef EMPTY_INTERFACE_FUNCTION
#define UNREACHABLE_INTERFACE_FUNCTION(name, ...) \
V8_INLINE void name(FullDecoder* decoder, ##__VA_ARGS__) { UNREACHABLE(); }
INTERFACE_NON_CONSTANT_FUNCTIONS(UNREACHABLE_INTERFACE_FUNCTION)
#undef EMPTY_INTERFACE_FUNCTION
#define DECLARE_INTERFACE_FUNCTION(name, ...) \

View File

@ -49,7 +49,7 @@ ValueType WasmInitExpr::type(const WasmModule* module,
return ValueType::Rtt(immediate().heap_type, 0);
case kRttSub:
case kRttFreshSub: {
ValueType operand_type = operands()[0].type(module, enabled_features);
ValueType operand_type = (*operands())[0].type(module, enabled_features);
if (!operand_type.is_rtt()) return kWasmBottom;
if (operand_type.has_depth()) {
return ValueType::Rtt(immediate().heap_type, operand_type.depth() + 1);

View File

@ -12,6 +12,7 @@
#include <memory>
#include "src/wasm/value-type.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
@ -21,7 +22,7 @@ struct WasmModule;
class WasmFeatures;
// Representation of an initializer expression.
class WasmInitExpr {
class WasmInitExpr : public ZoneObject {
public:
enum Operator {
kNone,
@ -54,25 +55,26 @@ class WasmInitExpr {
HeapType::Representation heap_type;
};
WasmInitExpr() : kind_(kNone) { immediate_.i32_const = 0; }
explicit WasmInitExpr(int32_t v) : kind_(kI32Const) {
WasmInitExpr() : kind_(kNone), operands_(nullptr) {
immediate_.i32_const = 0;
}
explicit WasmInitExpr(int32_t v) : kind_(kI32Const), operands_(nullptr) {
immediate_.i32_const = v;
}
explicit WasmInitExpr(int64_t v) : kind_(kI64Const) {
explicit WasmInitExpr(int64_t v) : kind_(kI64Const), operands_(nullptr) {
immediate_.i64_const = v;
}
explicit WasmInitExpr(float v) : kind_(kF32Const) {
explicit WasmInitExpr(float v) : kind_(kF32Const), operands_(nullptr) {
immediate_.f32_const = v;
}
explicit WasmInitExpr(double v) : kind_(kF64Const) {
explicit WasmInitExpr(double v) : kind_(kF64Const), operands_(nullptr) {
immediate_.f64_const = v;
}
explicit WasmInitExpr(uint8_t v[kSimd128Size]) : kind_(kS128Const) {
explicit WasmInitExpr(uint8_t v[kSimd128Size])
: kind_(kS128Const), operands_(nullptr) {
memcpy(immediate_.s128_const.data(), v, kSimd128Size);
}
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(WasmInitExpr);
static WasmInitExpr GlobalGet(uint32_t index) {
WasmInitExpr expr;
expr.kind_ = kGlobalGet;
@ -95,29 +97,25 @@ class WasmInitExpr {
}
static WasmInitExpr StructNewWithRtt(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kStructNewWithRtt;
ZoneVector<WasmInitExpr>* elements) {
WasmInitExpr expr(kStructNewWithRtt, elements);
expr.immediate_.index = index;
expr.operands_ = std::move(elements);
return expr;
}
static WasmInitExpr StructNew(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kStructNew;
ZoneVector<WasmInitExpr>* elements) {
WasmInitExpr expr(kStructNew, elements);
expr.immediate_.index = index;
expr.operands_ = std::move(elements);
return expr;
}
static WasmInitExpr StructNewDefaultWithRtt(uint32_t index,
static WasmInitExpr StructNewDefaultWithRtt(Zone* zone, uint32_t index,
WasmInitExpr rtt) {
WasmInitExpr expr;
expr.kind_ = kStructNewDefaultWithRtt;
WasmInitExpr expr(kStructNewDefaultWithRtt,
zone->New<ZoneVector<WasmInitExpr>>(
std::initializer_list<WasmInitExpr>{rtt}, zone));
expr.immediate_.index = index;
expr.operands_.push_back(std::move(rtt));
return expr;
}
@ -129,20 +127,16 @@ class WasmInitExpr {
}
static WasmInitExpr ArrayInit(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kArrayInit;
ZoneVector<WasmInitExpr>* elements) {
WasmInitExpr expr(kArrayInit, elements);
expr.immediate_.index = index;
expr.operands_ = std::move(elements);
return expr;
}
static WasmInitExpr ArrayInitStatic(uint32_t index,
std::vector<WasmInitExpr> elements) {
WasmInitExpr expr;
expr.kind_ = kArrayInitStatic;
ZoneVector<WasmInitExpr>* elements) {
WasmInitExpr expr(kArrayInitStatic, elements);
expr.immediate_.index = index;
expr.operands_ = std::move(elements);
return expr;
}
@ -153,25 +147,28 @@ class WasmInitExpr {
return expr;
}
static WasmInitExpr RttSub(uint32_t index, WasmInitExpr supertype) {
WasmInitExpr expr;
expr.kind_ = kRttSub;
static WasmInitExpr RttSub(Zone* zone, uint32_t index,
WasmInitExpr supertype) {
WasmInitExpr expr(
kRttSub, zone->New<ZoneVector<WasmInitExpr>>(
std::initializer_list<WasmInitExpr>{supertype}, zone));
expr.immediate_.index = index;
expr.operands_.push_back(std::move(supertype));
return expr;
}
static WasmInitExpr RttFreshSub(uint32_t index, WasmInitExpr supertype) {
WasmInitExpr expr;
expr.kind_ = kRttFreshSub;
static WasmInitExpr RttFreshSub(Zone* zone, uint32_t index,
WasmInitExpr supertype) {
WasmInitExpr expr(
kRttFreshSub,
zone->New<ZoneVector<WasmInitExpr>>(
std::initializer_list<WasmInitExpr>{supertype}, zone));
expr.immediate_.index = index;
expr.operands_.push_back(std::move(supertype));
return expr;
}
Immediate immediate() const { return immediate_; }
Operator kind() const { return kind_; }
const std::vector<WasmInitExpr>& operands() const { return operands_; }
const ZoneVector<WasmInitExpr>* operands() const { return operands_; }
bool operator==(const WasmInitExpr& other) const {
if (kind() != other.kind()) return false;
@ -199,16 +196,16 @@ class WasmInitExpr {
case kStructNewDefaultWithRtt:
case kStructNewDefault:
if (immediate().index != other.immediate().index) return false;
DCHECK_EQ(operands().size(), other.operands().size());
for (uint32_t i = 0; i < operands().size(); i++) {
DCHECK_EQ(operands()->size(), other.operands()->size());
for (uint32_t i = 0; i < operands()->size(); i++) {
if (operands()[i] != other.operands()[i]) return false;
}
return true;
case kArrayInit:
case kArrayInitStatic:
if (immediate().index != other.immediate().index) return false;
if (operands().size() != other.operands().size()) return false;
for (uint32_t i = 0; i < operands().size(); i++) {
if (operands()->size() != other.operands()->size()) return false;
for (uint32_t i = 0; i < operands()->size(); i++) {
if (operands()[i] != other.operands()[i]) return false;
}
return true;
@ -227,11 +224,15 @@ class WasmInitExpr {
const WasmFeatures& enabled_features) const;
private:
WasmInitExpr(Operator kind, const ZoneVector<WasmInitExpr>* operands)
: kind_(kind), operands_(operands) {}
Immediate immediate_;
Operator kind_;
std::vector<WasmInitExpr> operands_;
const ZoneVector<WasmInitExpr>* operands_;
};
ASSERT_TRIVIALLY_COPYABLE(WasmInitExpr);
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -355,7 +355,7 @@ uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size,
uint32_t WasmModuleBuilder::AddTable(ValueType type, uint32_t min_size,
uint32_t max_size, WasmInitExpr init) {
tables_.push_back({type, min_size, max_size, true, std::move(init)});
tables_.push_back({type, min_size, max_size, true, init});
return static_cast<uint32_t>(tables_.size() - 1);
}
@ -403,7 +403,7 @@ void WasmModuleBuilder::AddExport(base::Vector<const char> name,
uint32_t WasmModuleBuilder::AddExportedGlobal(ValueType type, bool mutability,
WasmInitExpr init,
base::Vector<const char> name) {
uint32_t index = AddGlobal(type, mutability, std::move(init));
uint32_t index = AddGlobal(type, mutability, init);
AddExport(name, kExternalGlobal, index);
return index;
}
@ -421,7 +421,7 @@ void WasmModuleBuilder::ExportImportedFunction(base::Vector<const char> name,
uint32_t WasmModuleBuilder::AddGlobal(ValueType type, bool mutability,
WasmInitExpr init) {
globals_.push_back({type, mutability, std::move(init)});
globals_.push_back({type, mutability, init});
return static_cast<uint32_t>(globals_.size() - 1);
}
@ -523,7 +523,7 @@ void WriteInitializerExpressionWithEnd(ZoneBuffer* buffer,
STATIC_ASSERT((kExprStructNewWithRtt >> 8) == kGCPrefix);
STATIC_ASSERT((kExprStructNewDefault >> 8) == kGCPrefix);
STATIC_ASSERT((kExprStructNewDefaultWithRtt >> 8) == kGCPrefix);
for (const WasmInitExpr& operand : init.operands()) {
for (const WasmInitExpr& operand : *init.operands()) {
WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom);
}
buffer->write_u8(kGCPrefix);
@ -551,7 +551,7 @@ void WriteInitializerExpressionWithEnd(ZoneBuffer* buffer,
case WasmInitExpr::kArrayInitStatic:
STATIC_ASSERT((kExprArrayInit >> 8) == kGCPrefix);
STATIC_ASSERT((kExprArrayInitStatic >> 8) == kGCPrefix);
for (const WasmInitExpr& operand : init.operands()) {
for (const WasmInitExpr& operand : *init.operands()) {
WriteInitializerExpressionWithEnd(buffer, operand, kWasmBottom);
}
buffer->write_u8(kGCPrefix);
@ -559,7 +559,7 @@ void WriteInitializerExpressionWithEnd(ZoneBuffer* buffer,
init.kind() == WasmInitExpr::kArrayInit ? kExprArrayInit
: kExprArrayInitStatic));
buffer->write_u32v(init.immediate().index);
buffer->write_u32v(static_cast<uint32_t>(init.operands().size() - 1));
buffer->write_u32v(static_cast<uint32_t>(init.operands()->size() - 1));
break;
case WasmInitExpr::kRttCanon:
STATIC_ASSERT((kExprRttCanon >> 8) == kGCPrefix);
@ -570,7 +570,7 @@ void WriteInitializerExpressionWithEnd(ZoneBuffer* buffer,
case WasmInitExpr::kRttSub:
case WasmInitExpr::kRttFreshSub:
// The operand to rtt.sub must be emitted first.
WriteInitializerExpressionWithEnd(buffer, init.operands()[0],
WriteInitializerExpressionWithEnd(buffer, (*init.operands())[0],
kWasmBottom);
STATIC_ASSERT((kExprRttSub >> 8) == kGCPrefix);
STATIC_ASSERT((kExprRttFreshSub >> 8) == kGCPrefix);

View File

@ -273,7 +273,7 @@ class V8_EXPORT_PRIVATE WasmModuleBuilder : public ZoneObject {
WasmInitExpr offset)
: type(type),
table_index(table_index),
offset(std::move(offset)),
offset(offset),
entries(zone),
status(kStatusActive) {
DCHECK(IsValidOffsetKind(offset.kind()));

View File

@ -462,7 +462,8 @@ int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset);
// Returns 0 if the type has no explicit supertype.
// The result is capped to {kV8MaxRttSubtypingDepth + 1}.
// Invalid cyclic hierarchies will return -1.
int GetSubtypingDepth(const WasmModule* module, uint32_t type_index);
V8_EXPORT_PRIVATE int GetSubtypingDepth(const WasmModule* module,
uint32_t type_index);
// Interface to the storage (wire bytes) of a wasm module.
// It is illegal for anyone receiving a ModuleWireBytes to store pointers based

View File

@ -50,7 +50,7 @@ class WasmGCTester {
}
byte AddGlobal(ValueType type, bool mutability, WasmInitExpr init) {
return builder_.AddGlobal(type, mutability, std::move(init));
return builder_.AddGlobal(type, mutability, init);
}
byte DefineFunction(FunctionSig* sig, std::initializer_list<ValueType> locals,
@ -1425,7 +1425,8 @@ WASM_COMPILED_EXEC_TEST(RttFreshSub) {
const byte kRtt = tester.AddGlobal(
ValueType::Rtt(kType, 1), false,
WasmInitExpr::RttFreshSub(type_repr, WasmInitExpr::RttCanon(type_repr)));
WasmInitExpr::RttFreshSub(tester.zone(), type_repr,
WasmInitExpr::RttCanon(type_repr)));
// A struct allocated with a fresh RTT does not match other fresh RTTs
// created for the same type.
@ -2082,7 +2083,8 @@ WASM_COMPILED_EXEC_TEST(CastsBenchmark) {
WasmInitExpr::RttCanon(static_cast<HeapType::Representation>(SuperType)));
const byte RttSub = tester.AddGlobal(
ValueType::Rtt(SubType, 1), false,
WasmInitExpr::RttSub(static_cast<HeapType::Representation>(SubType),
WasmInitExpr::RttSub(tester.zone(),
static_cast<HeapType::Representation>(SubType),
WasmInitExpr::GlobalGet(RttSuper)));
const byte RttList = tester.AddGlobal(
ValueType::Rtt(ListType, 0), false,

View File

@ -2297,7 +2297,8 @@ FunctionSig* GenerateSig(Zone* zone, DataRange* data, SigKind sig_kind,
return builder.Build();
}
WasmInitExpr GenerateInitExpr(WasmModuleBuilder* builder, ValueType type,
WasmInitExpr GenerateInitExpr(Zone* zone, WasmModuleBuilder* builder,
ValueType type,
uint32_t num_struct_and_array_types) {
switch (type.kind()) {
case kOptRef:
@ -2329,26 +2330,29 @@ WasmInitExpr GenerateInitExpr(WasmModuleBuilder* builder, ValueType type,
// We materialize all these types with a struct because they are all its
// supertypes.
DCHECK(builder->IsStructType(index));
std::vector<WasmInitExpr> elements;
ZoneVector<WasmInitExpr>* elements =
zone->New<ZoneVector<WasmInitExpr>>(zone);
int field_count = builder->GetStructType(index)->field_count();
for (int field_index = 0; field_index < field_count; field_index++) {
elements.push_back(GenerateInitExpr(
builder, builder->GetStructType(index)->field(field_index),
elements->push_back(GenerateInitExpr(
zone, builder, builder->GetStructType(index)->field(field_index),
num_struct_and_array_types));
}
return WasmInitExpr::StructNew(index, std::move(elements));
elements->push_back(WasmInitExpr::RttCanon(index));
return WasmInitExpr::StructNewWithRtt(index, elements);
}
DCHECK(type.has_index());
if (representation == HeapType::kFunc) {
return WasmInitExpr::RefFuncConst(index);
}
if (builder->IsArrayType(index)) {
std::vector<WasmInitExpr> elements;
elements.push_back(GenerateInitExpr(
builder, builder->GetArrayType(index)->element_type(),
ZoneVector<WasmInitExpr>* elements =
zone->New<ZoneVector<WasmInitExpr>>(zone);
elements->push_back(GenerateInitExpr(
zone, builder, builder->GetArrayType(index)->element_type(),
num_struct_and_array_types));
elements.push_back(WasmInitExpr::RttCanon(index));
return WasmInitExpr::ArrayInit(index, std::move(elements));
elements->push_back(WasmInitExpr::RttCanon(index));
return WasmInitExpr::ArrayInit(index, elements);
}
if (builder->IsSignature(index)) {
// Transform from signature index to function specific index.
@ -2460,7 +2464,7 @@ class WasmCompileFuzzer : public WasmExecutionFuzzer {
builder.AddGlobal(
type, mutability,
GenerateInitExpr(&builder, type,
GenerateInitExpr(zone, &builder, type,
static_cast<uint32_t>(num_structs + num_arrays)));
globals.push_back(type);
if (mutability) mutable_globals.push_back(static_cast<uint8_t>(i));

View File

@ -22,6 +22,7 @@
#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "src/wasm/wasm-opcodes-inl.h"
#include "src/zone/accounting-allocator.h"
#include "src/zone/zone.h"
#include "test/common/wasm/flag-utils.h"
@ -313,68 +314,234 @@ std::ostream& operator<<(std::ostream& os, WasmElemSegment::Entry entry) {
return os << ")";
}
// An interface for WasmFullDecoder used to decode initializer expressions. As
// opposed to the one in src/wasm/, this emits {WasmInitExpr} as opposed to a
// {WasmValue}.
class InitExprInterface {
public:
static constexpr Decoder::ValidateFlag validate = Decoder::kFullValidation;
static constexpr DecodingMode decoding_mode = kInitExpression;
struct Value : public ValueBase<validate> {
WasmInitExpr init_expr;
template <typename... Args>
explicit Value(Args&&... args) V8_NOEXCEPT
: ValueBase(std::forward<Args>(args)...) {}
};
using Control = ControlBase<Value, validate>;
using FullDecoder =
WasmFullDecoder<validate, InitExprInterface, decoding_mode>;
explicit InitExprInterface(Zone* zone) : zone_(zone) {}
#define EMPTY_INTERFACE_FUNCTION(name, ...) \
V8_INLINE void name(FullDecoder* decoder, ##__VA_ARGS__) {}
INTERFACE_META_FUNCTIONS(EMPTY_INTERFACE_FUNCTION)
#undef EMPTY_INTERFACE_FUNCTION
#define UNREACHABLE_INTERFACE_FUNCTION(name, ...) \
V8_INLINE void name(FullDecoder* decoder, ##__VA_ARGS__) { UNREACHABLE(); }
INTERFACE_NON_CONSTANT_FUNCTIONS(UNREACHABLE_INTERFACE_FUNCTION)
#undef UNREACHABLE_INTERFACE_FUNCTION
void I32Const(FullDecoder* decoder, Value* result, int32_t value) {
result->init_expr = WasmInitExpr(value);
}
void I64Const(FullDecoder* decoder, Value* result, int64_t value) {
result->init_expr = WasmInitExpr(value);
}
void F32Const(FullDecoder* decoder, Value* result, float value) {
result->init_expr = WasmInitExpr(value);
}
void F64Const(FullDecoder* decoder, Value* result, double value) {
result->init_expr = WasmInitExpr(value);
}
void S128Const(FullDecoder* decoder, Simd128Immediate<validate>& imm,
Value* result) {
result->init_expr = WasmInitExpr(imm.value);
}
void RefNull(FullDecoder* decoder, ValueType type, Value* result) {
result->init_expr = WasmInitExpr::RefNullConst(type.heap_representation());
}
void RefFunc(FullDecoder* decoder, uint32_t function_index, Value* result) {
result->init_expr = WasmInitExpr::RefFuncConst(function_index);
}
void GlobalGet(FullDecoder* decoder, Value* result,
const GlobalIndexImmediate<validate>& imm) {
result->init_expr = WasmInitExpr::GlobalGet(imm.index);
}
void StructNewWithRtt(FullDecoder* decoder,
const StructIndexImmediate<validate>& imm,
const Value& rtt, const Value args[], Value* result) {
ZoneVector<WasmInitExpr>* elements =
zone_->New<ZoneVector<WasmInitExpr>>(zone_);
for (size_t i = 0; i < imm.struct_type->field_count(); i++) {
elements->push_back(args[i].init_expr);
}
bool nominal = decoder->module_->has_supertype(imm.index);
if (!nominal) elements->push_back(rtt.init_expr);
result->init_expr =
nominal ? WasmInitExpr::StructNew(imm.index, elements)
: WasmInitExpr::StructNewWithRtt(imm.index, elements);
}
void StructNewDefault(FullDecoder* decoder,
const StructIndexImmediate<validate>& imm,
const Value& rtt, Value* result) {
bool nominal = decoder->module_->has_supertype(imm.index);
result->init_expr = nominal ? WasmInitExpr::StructNewDefault(imm.index)
: WasmInitExpr::StructNewDefaultWithRtt(
zone_, imm.index, rtt.init_expr);
}
void ArrayInit(FullDecoder* decoder, const ArrayIndexImmediate<validate>& imm,
const base::Vector<Value>& elements, const Value& rtt,
Value* result) {
ZoneVector<WasmInitExpr>* args =
zone_->New<ZoneVector<WasmInitExpr>>(zone_);
for (Value expr : elements) args->push_back(expr.init_expr);
bool nominal = decoder->module_->has_supertype(imm.index);
if (!nominal) args->push_back(rtt.init_expr);
result->init_expr = nominal ? WasmInitExpr::ArrayInitStatic(imm.index, args)
: WasmInitExpr::ArrayInit(imm.index, args);
}
void RttCanon(FullDecoder* decoder, uint32_t type_index, Value* result) {
result->init_expr = WasmInitExpr::RttCanon(type_index);
}
void RttSub(FullDecoder* decoder, uint32_t type_index, const Value& parent,
Value* result, WasmRttSubMode mode) {
result->init_expr =
WasmInitExpr::RttSub(zone_, type_index, parent.init_expr);
}
void DoReturn(FullDecoder* decoder, uint32_t /*drop_values*/) {
// End decoding on "end".
decoder->set_end(decoder->pc() + 1);
result_ = decoder->stack_value(1)->init_expr;
}
WasmInitExpr result() { return result_; }
private:
WasmInitExpr result_;
Zone* zone_;
};
// Appends an initializer expression encoded in {wire_bytes}, in the offset
// contained in {expr}.
void AppendInitExpr(std::ostream& os, ModuleWireBytes wire_bytes,
WireBytesRef expr) {
Decoder decoder(wire_bytes.module_bytes());
const byte* pc = wire_bytes.module_bytes().begin() + expr.offset();
uint32_t length;
void AppendInitExpr(std::ostream& os, const WasmInitExpr& expr) {
os << "WasmInitExpr.";
switch (static_cast<WasmOpcode>(pc[0])) {
case kExprGlobalGet:
os << "GlobalGet("
<< decoder.read_u32v<Decoder::kNoValidation>(pc + 1, &length);
bool append_operands = false;
switch (expr.kind()) {
case WasmInitExpr::kNone:
UNREACHABLE();
case WasmInitExpr::kGlobalGet:
os << "GlobalGet(" << expr.immediate().index;
break;
case kExprI32Const:
os << "I32Const("
<< decoder.read_i32v<Decoder::kNoValidation>(pc + 1, &length);
case WasmInitExpr::kI32Const:
os << "I32Const(" << expr.immediate().i32_const;
break;
case kExprI64Const:
os << "I64Const("
<< decoder.read_i64v<Decoder::kNoValidation>(pc + 1, &length);
case WasmInitExpr::kI64Const:
os << "I64Const(" << expr.immediate().i64_const;
break;
case kExprF32Const: {
uint32_t result = decoder.read_u32<Decoder::kNoValidation>(pc + 1);
os << "F32Const(" << bit_cast<float>(result);
case WasmInitExpr::kF32Const:
os << "F32Const(" << expr.immediate().f32_const;
break;
}
case kExprF64Const: {
uint64_t result = decoder.read_u64<Decoder::kNoValidation>(pc + 1);
os << "F64Const(" << bit_cast<double>(result);
case WasmInitExpr::kF64Const:
os << "F64Const(" << expr.immediate().f64_const;
break;
}
case kSimdPrefix: {
DCHECK_LE(2 + kSimd128Size, expr.length());
DCHECK_EQ(static_cast<WasmOpcode>(pc[1]), kExprS128Const & 0xff);
case WasmInitExpr::kS128Const:
os << "S128Const([";
for (int i = 0; i < kSimd128Size; i++) {
os << int(decoder.read_u8<Decoder::kNoValidation>(pc + 2 + i));
if (i + 1 < kSimd128Size) os << ", ";
os << static_cast<int>(expr.immediate().s128_const[i]);
if (i < kSimd128Size - 1) os << ", ";
}
os << "]";
break;
}
case kExprRefFunc:
os << "RefFunc("
<< decoder.read_u32v<Decoder::kNoValidation>(pc + 1, &length);
case WasmInitExpr::kRefNullConst:
os << "RefNull("
<< HeapTypeToConstantName(HeapType(expr.immediate().heap_type));
break;
case kExprRefNull: {
HeapType heap_type =
value_type_reader::read_heap_type<Decoder::kNoValidation>(
&decoder, pc + 1, &length, nullptr, WasmFeatures::All());
os << "RefNull(" << HeapTypeToConstantName(heap_type);
case WasmInitExpr::kRefFuncConst:
os << "RefFunc(" << expr.immediate().index;
break;
case WasmInitExpr::kStructNewWithRtt:
os << "StructNewWithRtt(" << expr.immediate().index;
append_operands = true;
break;
case WasmInitExpr::kStructNew:
os << "StructNew(" << expr.immediate().index;
append_operands = true;
break;
case WasmInitExpr::kStructNewDefaultWithRtt:
os << "StructNewDefaultWithRtt(" << expr.immediate().index << ", ";
AppendInitExpr(os, (*expr.operands())[0]);
break;
case WasmInitExpr::kStructNewDefault:
os << "StructNewDefault(" << expr.immediate().index;
break;
case WasmInitExpr::kArrayInit:
os << "ArrayInit(" << expr.immediate().index;
append_operands = true;
break;
case WasmInitExpr::kArrayInitStatic:
os << "ArrayInitStatic(" << expr.immediate().index;
append_operands = true;
break;
case WasmInitExpr::kRttCanon:
os << "RttCanon(" << expr.immediate().index;
break;
case WasmInitExpr::kRttSub:
os << "RttSub(" << expr.immediate().index << ", ";
AppendInitExpr(os, (*expr.operands())[0]);
break;
case WasmInitExpr::kRttFreshSub:
os << "RttFreshSub(" << expr.immediate().index << ", ";
AppendInitExpr(os, (*expr.operands())[0]);
break;
}
case kExprStructNew:
UNIMPLEMENTED();
case kExprArrayInit:
UNIMPLEMENTED();
default:
UNREACHABLE();
}
if (append_operands) {
os << ", [";
for (size_t i = 0; i < expr.operands()->size(); i++) {
AppendInitExpr(os, (*expr.operands())[i]);
if (i < expr.operands()->size() - 1) os << ", ";
}
os << "]";
}
os << ")";
}
void DecodeAndAppendInitExpr(StdoutStream& os, Zone* zone,
const WasmModule* module,
ModuleWireBytes module_bytes, WireBytesRef init,
ValueType expected) {
FunctionBody body(FunctionSig::Build(zone, {expected}, {}), init.offset(),
module_bytes.start() + init.offset(),
module_bytes.start() + init.end_offset());
WasmFeatures detected;
WasmFullDecoder<Decoder::kFullValidation, InitExprInterface, kInitExpression>
decoder(zone, module, WasmFeatures::All(), &detected, body, zone);
decoder.DecodeFunctionBody();
AppendInitExpr(os, decoder.interface().result());
}
} // namespace
void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
@ -390,6 +557,9 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
WasmModule* module = module_res.value().get();
CHECK_NOT_NULL(module);
AccountingAllocator allocator;
Zone zone(&allocator, "init. expression zone");
StdoutStream os;
tzset();
@ -428,10 +598,11 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
os << ");\n";
}
for (WasmGlobal& glob : module->globals) {
os << "builder.addGlobal(" << ValueTypeToConstantName(glob.type) << ", "
<< glob.mutability << ", ";
AppendInitExpr(os, wire_bytes, glob.init);
for (WasmGlobal& global : module->globals) {
os << "builder.addGlobal(" << ValueTypeToConstantName(global.type) << ", "
<< global.mutability << ", ";
DecodeAndAppendInitExpr(os, &zone, module, wire_bytes, global.init,
global.type);
os << ");\n";
}
@ -451,15 +622,13 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
os << "makeField(" << ValueTypeToConstantName(struct_type->field(index))
<< ", " << (struct_type->mutability(index) ? "true" : "false")
<< ")";
if (index + 1 < field_count)
os << ", ";
else
os << "]);\n";
if (index + 1 < field_count) os << ", ";
}
os << "]);\n";
} else if (module->has_array(i)) {
const ArrayType* array_type = module->types[i].array_type;
os << "builder.addArray("
<< ValueTypeToConstantName(array_type->element_type()) << ","
<< ValueTypeToConstantName(array_type->element_type()) << ", "
<< (array_type->mutability() ? "true" : "false") << ");\n";
} else {
DCHECK(module->has_signature(i));
@ -489,7 +658,8 @@ void GenerateTestCase(Isolate* isolate, ModuleWireBytes wire_bytes,
os << "builder.add" << status_str << "ElementSegment(";
if (elem_segment.status == WasmElemSegment::kStatusActive) {
os << elem_segment.table_index << ", ";
AppendInitExpr(os, wire_bytes, elem_segment.offset);
DecodeAndAppendInitExpr(os, &zone, module, wire_bytes,
elem_segment.offset, kWasmI32);
os << ", ";
}
os << "[";