Skip to content

Commit d7f7fd7

Browse files
authored
Segment flags should be MVP-compat when possible (#1286)
This should fix #1280, #1268, #1269.
1 parent ea8ce4f commit d7f7fd7

16 files changed

+302
-114
lines changed

src/binary-reader-ir.cc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,7 +1016,13 @@ Result BinaryReaderIR::BeginElemSegment(Index index,
10161016
auto field = MakeUnique<ElemSegmentModuleField>(GetLocation());
10171017
ElemSegment& elem_segment = field->elem_segment;
10181018
elem_segment.table_var = Var(table_index, GetLocation());
1019-
elem_segment.flags = flags;
1019+
if ((flags & SegDeclared) == SegDeclared) {
1020+
elem_segment.kind = SegmentKind::Declared;
1021+
} else if ((flags & SegPassive) == SegPassive) {
1022+
elem_segment.kind = SegmentKind::Passive;
1023+
} else {
1024+
elem_segment.kind = SegmentKind::Active;
1025+
}
10201026
elem_segment.elem_type = elem_type;
10211027
module_->AppendField(std::move(field));
10221028
return Result::Ok;
@@ -1071,7 +1077,11 @@ Result BinaryReaderIR::BeginDataSegment(Index index,
10711077
auto field = MakeUnique<DataSegmentModuleField>(GetLocation());
10721078
DataSegment& data_segment = field->data_segment;
10731079
data_segment.memory_var = Var(memory_index, GetLocation());
1074-
data_segment.flags = flags;
1080+
if ((flags & SegPassive) == SegPassive) {
1081+
data_segment.kind = SegmentKind::Passive;
1082+
} else {
1083+
data_segment.kind = SegmentKind::Active;
1084+
}
10751085
module_->AppendField(std::move(field));
10761086
return Result::Ok;
10771087
}

src/binary-reader.cc

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,10 +2168,11 @@ Result BinaryReader::ReadElemSection(Offset section_size) {
21682168
for (Index i = 0; i < num_elem_segments; ++i) {
21692169
uint32_t flags;
21702170
CHECK_RESULT(ReadU32Leb128(&flags, "elem segment flags"));
2171-
ERROR_IF(flags > ~(~0u << SegFlagMax), "invalid elem segment flags: %#x",
2172-
flags);
2171+
ERROR_IF(flags > SegFlagMax, "invalid elem segment flags: %#x", flags);
2172+
ERROR_IF((flags & SegDeclared) == SegDeclared,
2173+
"declared segments aren't supported");
21732174
Index table_index(0);
2174-
if (flags & SegExplicitIndex) {
2175+
if ((flags & (SegPassive | SegExplicitIndex)) == SegExplicitIndex) {
21752176
CHECK_RESULT(ReadIndex(&table_index, "elem segment table index"));
21762177
}
21772178
Type elem_type = Type::Funcref;
@@ -2185,8 +2186,7 @@ Result BinaryReader::ReadElemSection(Offset section_size) {
21852186
}
21862187

21872188
// For backwards compat we support not declaring the element kind.
2188-
bool legacy = !(flags & SegPassive) && !(flags & SegExplicitIndex);
2189-
if (!legacy) {
2189+
if (flags & (SegPassive | SegExplicitIndex)) {
21902190
if (flags & SegUseElemExprs) {
21912191
CHECK_RESULT(ReadType(&elem_type, "table elem type"));
21922192
ERROR_UNLESS(IsRefType(elem_type),
@@ -2288,8 +2288,7 @@ Result BinaryReader::ReadDataSection(Offset section_size) {
22882288
for (Index i = 0; i < num_data_segments; ++i) {
22892289
uint32_t flags;
22902290
CHECK_RESULT(ReadU32Leb128(&flags, "data segment flags"));
2291-
ERROR_IF(flags > ~(~0u << SegFlagMax), "invalid data segment flags: %#x",
2292-
flags);
2291+
ERROR_IF(flags > SegFlagMax, "invalid data segment flags: %#x", flags);
22932292
Index memory_index(0);
22942293
if (flags & SegExplicitIndex) {
22952294
CHECK_RESULT(ReadIndex(&memory_index, "data segment memory index"));

src/binary-writer.cc

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,30 +1055,29 @@ Result BinaryWriter::WriteModule() {
10551055
ElemSegment* segment = module_->elem_segments[i];
10561056
WriteHeader("elem segment header", i);
10571057
// 1. flags
1058-
stream_->WriteU8(segment->flags, "segment flags");
1058+
uint8_t flags = segment->GetFlags(module_);
1059+
stream_->WriteU8(flags, "segment flags");
10591060
// 2. optional target table
1060-
if (segment->flags & SegExplicitIndex) {
1061-
WriteU32Leb128(stream_, module_->GetTableIndex(segment->table_var), "table index");
1061+
if (flags & SegExplicitIndex) {
1062+
WriteU32Leb128(stream_, module_->GetTableIndex(segment->table_var),
1063+
"table index");
10621064
}
10631065
// 3. optional target location within the table (active segments only)
1064-
if (!segment->is_passive()) {
1066+
if (!(flags & SegPassive)) {
10651067
WriteInitExpr(segment->offset);
10661068
}
1067-
// 4. type of item in the following list
1068-
bool legacy =
1069-
!segment->is_passive() && !(segment->flags & SegExplicitIndex);
1070-
if (!legacy) {
1071-
if (segment->flags & SegUseElemExprs) {
1069+
// 4. type of item in the following list (omitted for "legacy" segments)
1070+
if (flags & (SegPassive | SegExplicitIndex)) {
1071+
if (flags & SegUseElemExprs) {
10721072
WriteType(stream_, segment->elem_type, "elem expr list type");
10731073
} else {
1074-
assert(segment->elem_type == Type::Funcref);
10751074
stream_->WriteU8Enum(ExternalKind::Func, "elem list type");
10761075
}
10771076
}
10781077
// 5. actual list of elements (with extern indexes or elem expr's)
10791078
// preceeded by length
10801079
WriteU32Leb128(stream_, segment->elem_exprs.size(), "num elems");
1081-
if (segment->flags & SegUseElemExprs) {
1080+
if (flags & SegUseElemExprs) {
10821081
for (const ElemExpr& elem_expr : segment->elem_exprs) {
10831082
switch (elem_expr.kind) {
10841083
case ElemExprKind::RefNull:
@@ -1137,8 +1136,9 @@ Result BinaryWriter::WriteModule() {
11371136
for (size_t i = 0; i < module_->data_segments.size(); ++i) {
11381137
const DataSegment* segment = module_->data_segments[i];
11391138
WriteHeader("data segment header", i);
1140-
stream_->WriteU8(segment->flags, "segment flags");
1141-
if (!segment->is_passive()) {
1139+
uint8_t flags = segment->GetFlags(module_);
1140+
stream_->WriteU8(flags, "segment flags");
1141+
if (!(flags & SegPassive)) {
11421142
assert(module_->GetMemoryIndex(segment->memory_var) == 0);
11431143
WriteInitExpr(segment->offset);
11441144
}

src/common.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,14 +229,21 @@ enum class Type : int32_t {
229229
};
230230
typedef std::vector<Type> TypeVector;
231231

232+
enum class SegmentKind {
233+
Active,
234+
Passive,
235+
Declared,
236+
};
237+
232238
// Matches binary format, do not change.
233239
enum SegmentFlags : uint8_t {
234240
SegFlagsNone = 0,
235241
SegPassive = 1, // bit 0: Is passive
236-
SegExplicitIndex = 2, // bit 1: Has explict index (Inplies table 0 if absent)
242+
SegExplicitIndex = 2, // bit 1: Has explict index (Implies table 0 if absent)
243+
SegDeclared = 3, // Only used for declared segments
237244
SegUseElemExprs = 4, // bit 2: Is elemexpr (Or else index sequence)
238245

239-
SegFlagMax = SegUseElemExprs,
246+
SegFlagMax = (SegUseElemExprs << 1) - 1, // All bits set.
240247
};
241248

242249
enum class RelocType {

src/ir.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,4 +651,52 @@ Const::Const(F64Tag, uint64_t value, const Location& loc_)
651651
Const::Const(V128Tag, v128 value, const Location& loc_)
652652
: loc(loc_), type(Type::V128), vec128(value) {}
653653

654+
uint8_t ElemSegment::GetFlags(const Module* module) const {
655+
uint8_t flags = 0;
656+
657+
switch (kind) {
658+
case SegmentKind::Active: {
659+
Index table_index = module->GetTableIndex(table_var);
660+
if (table_index != 0) {
661+
flags |= SegExplicitIndex;
662+
}
663+
break;
664+
}
665+
666+
case SegmentKind::Passive:
667+
flags |= SegPassive;
668+
break;
669+
670+
case SegmentKind::Declared:
671+
flags |= SegDeclared;
672+
break;
673+
}
674+
675+
bool all_ref_func = std::all_of(
676+
elem_exprs.begin(), elem_exprs.end(), [](const ElemExpr& elem_expr) {
677+
return elem_expr.kind == ElemExprKind::RefFunc;
678+
});
679+
if (!all_ref_func) {
680+
flags |= SegUseElemExprs;
681+
}
682+
683+
return flags;
684+
}
685+
686+
uint8_t DataSegment::GetFlags(const Module* module) const {
687+
uint8_t flags = 0;
688+
689+
if (kind == SegmentKind::Passive) {
690+
flags |= SegPassive;
691+
}
692+
693+
Index memory_index = module->GetMemoryIndex(memory_var);
694+
if (memory_index != 0) {
695+
flags |= SegExplicitIndex;
696+
}
697+
698+
return flags;
699+
}
700+
701+
654702
} // namespace wabt

src/ir.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333

3434
namespace wabt {
3535

36+
struct Module;
37+
3638
enum class VarType {
3739
Index,
3840
Name,
@@ -600,11 +602,11 @@ typedef std::vector<ElemExpr> ElemExprVector;
600602

601603
struct ElemSegment {
602604
explicit ElemSegment(string_view name) : name(name.to_string()) {}
603-
bool is_passive() const { return flags & SegPassive; }
605+
uint8_t GetFlags(const Module*) const;
604606

607+
SegmentKind kind = SegmentKind::Active;
605608
std::string name;
606609
Var table_var;
607-
uint8_t flags = 0;
608610
Type elem_type;
609611
ExprList offset;
610612
ElemExprVector elem_exprs;
@@ -619,11 +621,11 @@ struct Memory {
619621

620622
struct DataSegment {
621623
explicit DataSegment(string_view name) : name(name.to_string()) {}
622-
bool is_passive() const { return flags & SegPassive; }
624+
uint8_t GetFlags(const Module*) const;
623625

626+
SegmentKind kind = SegmentKind::Active;
624627
std::string name;
625628
Var memory_var;
626-
uint8_t flags = 0;
627629
ExprList offset;
628630
std::vector<uint8_t> data;
629631
};

src/validator.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,16 +1152,24 @@ void Validator::CheckTable(const Location* loc, const Table* table) {
11521152
}
11531153

11541154
void Validator::CheckElemSegments(const Module* module) {
1155+
bool bulk_or_ref_enabled = options_.features.bulk_memory_enabled() ||
1156+
options_.features.reference_types_enabled();
1157+
11551158
for (const ModuleField& field : module->fields) {
11561159
if (auto elem_segment_field = dyn_cast<ElemSegmentModuleField>(&field)) {
11571160
auto&& elem_segment = elem_segment_field->elem_segment;
11581161
for (const ElemExpr& elem_expr : elem_segment.elem_exprs) {
11591162
if (elem_expr.kind == ElemExprKind::RefFunc) {
11601163
CheckFuncVar(&elem_expr.var, nullptr);
1164+
} else {
1165+
assert(elem_expr.kind == ElemExprKind::RefNull);
1166+
if (!bulk_or_ref_enabled) {
1167+
PrintError(&field.loc, "ref.null is not allowed");
1168+
}
11611169
}
11621170
}
11631171

1164-
if (elem_segment.is_passive()) {
1172+
if (elem_segment.kind == SegmentKind::Passive) {
11651173
continue;
11661174
}
11671175
if (Failed(CheckTableVar(&elem_segment.table_var, nullptr))) {
@@ -1193,7 +1201,7 @@ void Validator::CheckDataSegments(const Module* module) {
11931201
if (auto data_segment_field = dyn_cast<DataSegmentModuleField>(&field)) {
11941202
auto&& data_segment = data_segment_field->data_segment;
11951203
const Memory* memory;
1196-
if (data_segment.is_passive()) {
1204+
if (data_segment.kind == SegmentKind::Passive) {
11971205
continue;
11981206
}
11991207
if (Failed(CheckMemoryVar(&data_segment.memory_var, &memory))) {

src/wast-parser.cc

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -933,12 +933,12 @@ Result WastParser::ParseDataModuleField(Module* module) {
933933
} else if (ParseVarOpt(&field->data_segment.memory_var, Var(0, loc))) {
934934
CHECK_RESULT(ParseOffsetExpr(&field->data_segment.offset));
935935
} else if (!ParseOffsetExprOpt(&field->data_segment.offset)) {
936-
if (options_->features.bulk_memory_enabled()) {
937-
field->data_segment.flags |= SegPassive;
938-
} else {
936+
if (!options_->features.bulk_memory_enabled()) {
939937
Error(loc, "passive data segments are not allowed");
940938
return Result::Error;
941939
}
940+
941+
field->data_segment.kind = SegmentKind::Passive;
942942
}
943943

944944
ParseTextListOpt(&field->data_segment.data);
@@ -956,7 +956,7 @@ Result WastParser::ParseElemModuleField(Module* module) {
956956
// With MVP text format the name here was intended to refer to the table
957957
// that the elem segment was part of, but we never did anything with this name
958958
// since there was only one table anyway.
959-
// With bulk-memory enabled this introduces a new name for the particualr
959+
// With bulk-memory enabled this introduces a new name for the particular
960960
// elem segment.
961961
std::string initial_name;
962962
bool has_name = ParseBindVarOpt(&initial_name);
@@ -970,7 +970,6 @@ Result WastParser::ParseElemModuleField(Module* module) {
970970
// Optional table specifier
971971
if (options_->features.bulk_memory_enabled()) {
972972
if (PeekMatchLpar(TokenType::Table)) {
973-
field->elem_segment.flags |= SegExplicitIndex;
974973
EXPECT(Lpar);
975974
EXPECT(Table);
976975
CHECK_RESULT(ParseVar(&field->elem_segment.table_var));
@@ -981,23 +980,25 @@ Result WastParser::ParseElemModuleField(Module* module) {
981980
} else {
982981
if (has_name) {
983982
field->elem_segment.table_var = Var(initial_name, loc);
984-
field->elem_segment.flags |= SegExplicitIndex;
985983
} else {
986984
ParseVarOpt(&field->elem_segment.table_var, Var(0, loc));
987985
}
988986
}
989987

990988
if (!ParseOffsetExprOpt(&field->elem_segment.offset)) {
991-
field->elem_segment.flags |= SegPassive;
989+
field->elem_segment.kind = SegmentKind::Passive;
992990
}
993991

994992
if (ParseRefTypeOpt(&field->elem_segment.elem_type)) {
995-
// TODO: Don't allow unless bulk memory is enabled.
996-
field->elem_segment.flags |= (SegPassive | SegUseElemExprs);
997993
// Parse a potentially empty sequence of ElemExprs.
998994
while (true) {
999995
Var var;
996+
Location loc = GetLocation();
1000997
if (MatchLpar(TokenType::RefNull)) {
998+
if (!(options_->features.bulk_memory_enabled() ||
999+
options_->features.reference_types_enabled())) {
1000+
Error(loc, "ref.null not allowed");
1001+
}
10011002
field->elem_segment.elem_exprs.emplace_back();
10021003
EXPECT(Rpar);
10031004
} else if (MatchLpar(TokenType::RefFunc)) {
@@ -1319,8 +1320,6 @@ Result WastParser::ParseTableModuleField(Module* module) {
13191320
auto elem_segment_field = MakeUnique<ElemSegmentModuleField>(loc);
13201321
ElemSegment& elem_segment = elem_segment_field->elem_segment;
13211322
elem_segment.table_var = Var(module->tables.size());
1322-
if (module->tables.size() > 0)
1323-
elem_segment.flags |= SegExplicitIndex;
13241323
elem_segment.offset.push_back(MakeUnique<ConstExpr>(Const::I32(0)));
13251324
elem_segment.offset.back().loc = loc;
13261325
elem_segment.elem_type = elem_type;

src/wat-writer.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,19 +1258,21 @@ void WatWriter::WriteElemSegment(const ElemSegment& segment) {
12581258
WriteOpenSpace("elem");
12591259
WriteNameOrIndex(segment.name, elem_segment_index_, NextChar::Space);
12601260

1261-
if (!segment.is_passive()) {
1261+
uint8_t flags = segment.GetFlags(&module);
1262+
1263+
if (!(flags & SegPassive)) {
12621264
WriteInitExpr(segment.offset);
12631265
}
12641266

1265-
if (segment.flags & SegUseElemExprs) {
1267+
if (flags & SegUseElemExprs) {
12661268
WriteType(segment.elem_type, NextChar::Space);
12671269
} else {
12681270
assert(segment.elem_type == Type::Funcref);
12691271
WritePuts("func", NextChar::Space);
12701272
}
12711273

12721274
for (const ElemExpr& expr : segment.elem_exprs) {
1273-
if (segment.flags & SegUseElemExprs) {
1275+
if (flags & SegUseElemExprs) {
12741276
if (expr.kind == ElemExprKind::RefNull) {
12751277
WriteOpenSpace("ref.null");
12761278
WriteCloseSpace();
@@ -1301,7 +1303,7 @@ void WatWriter::WriteMemory(const Memory& memory) {
13011303
void WatWriter::WriteDataSegment(const DataSegment& segment) {
13021304
WriteOpenSpace("data");
13031305
WriteNameOrIndex(segment.name, data_segment_index_, NextChar::Space);
1304-
if (!segment.is_passive()) {
1306+
if (segment.kind != SegmentKind::Passive) {
13051307
WriteInitExpr(segment.offset);
13061308
}
13071309
WriteQuotedData(segment.data.data(), segment.data.size());
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
;;; TOOL: run-gen-wasm-bad
2+
magic
3+
version
4+
section(TABLE) {
5+
count[1]
6+
anyfunc
7+
has_max[0]
8+
initial[0]
9+
}
10+
section(ELEM) {
11+
count[1]
12+
flags[3]
13+
}
14+
(;; STDERR ;;;
15+
0000012: error: declared segments aren't supported
16+
0000012: error: declared segments aren't supported
17+
;;; STDERR ;;)

0 commit comments

Comments
 (0)