From 3c79f17a7dadb396f955f0912858c3230ed8e10e Mon Sep 17 00:00:00 2001 From: Wouter van Oortmerssen Date: Mon, 5 Apr 2021 12:38:16 -0700 Subject: [PATCH] Memory64: support 64-bit data init-expr (#1656) --- src/binary-reader.cc | 22 +++++++++++----------- src/common.h | 1 + src/interp/binary-reader-interp.cc | 4 ++++ src/interp/interp.cc | 9 ++++++--- src/shared-validator.cc | 10 +++++++--- src/type-checker.cc | 2 +- src/wast-parser.cc | 3 ++- test/interp/load64.txt | 8 ++++---- test/regress/regress-27.txt | 2 +- test/spec/data.txt | 2 +- third_party/testsuite | 2 +- 11 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/binary-reader.cc b/src/binary-reader.cc index a23ec3b22..8446c1971 100644 --- a/src/binary-reader.cc +++ b/src/binary-reader.cc @@ -118,8 +118,7 @@ class BinaryReader { Index NumTotalFuncs(); - Result ReadI32InitExpr(Index index) WABT_WARN_UNUSED; - Result ReadInitExpr(Index index, bool require_i32 = false) WABT_WARN_UNUSED; + Result ReadInitExpr(Index index, Type required = Type::Any) WABT_WARN_UNUSED; Result ReadTable(Type* out_elem_type, Limits* out_elem_limits) WABT_WARN_UNUSED; Result ReadMemory(Limits* out_page_limits) WABT_WARN_UNUSED; @@ -468,11 +467,7 @@ Index BinaryReader::NumTotalFuncs() { return num_func_imports_ + num_function_signatures_; } -Result BinaryReader::ReadI32InitExpr(Index index) { - return ReadInitExpr(index, true); -} - -Result BinaryReader::ReadInitExpr(Index index, bool require_i32) { +Result BinaryReader::ReadInitExpr(Index index, Type required) { Opcode opcode; CHECK_RESULT(ReadOpcode(&opcode, "opcode")); ERROR_UNLESS_OPCODE_ENABLED(opcode); @@ -542,11 +537,16 @@ Result BinaryReader::ReadInitExpr(Index index, bool require_i32) { return ReportUnexpectedOpcode(opcode, "in initializer expression"); } - if (require_i32 && opcode != Opcode::I32Const && - opcode != Opcode::GlobalGet) { + if (required == Type::I32 && opcode != Opcode::I32Const && + opcode != Opcode::GlobalGet) { PrintError("expected i32 init_expr"); return Result::Error; } + if (required == Type::I64 && opcode != Opcode::I64Const && + opcode != Opcode::GlobalGet) { + PrintError("expected i64 init_expr"); + return Result::Error; + } CHECK_RESULT(ReadOpcode(&opcode, "opcode")); ERROR_UNLESS(opcode == Opcode::End, @@ -2443,7 +2443,7 @@ Result BinaryReader::ReadElemSection(Offset section_size) { if (!(flags & SegPassive)) { CALLBACK(BeginElemSegmentInitExpr, i); - CHECK_RESULT(ReadI32InitExpr(i)); + CHECK_RESULT(ReadInitExpr(i, Type::I32)); CALLBACK(EndElemSegmentInitExpr, i); } @@ -2559,7 +2559,7 @@ Result BinaryReader::ReadDataSection(Offset section_size) { CALLBACK(BeginDataSegment, i, memory_index, flags); if (!(flags & SegPassive)) { CALLBACK(BeginDataSegmentInitExpr, i); - CHECK_RESULT(ReadI32InitExpr(i)); + CHECK_RESULT(ReadInitExpr(i, memories[0].IndexType())); CALLBACK(EndDataSegmentInitExpr, i); } diff --git a/src/common.h b/src/common.h index dbac0a895..e0b50f17b 100644 --- a/src/common.h +++ b/src/common.h @@ -394,6 +394,7 @@ struct Limits { has_max(true), is_shared(is_shared), is_64(is_64) {} + Type IndexType() const { return is_64 ? Type::I64 : Type::I32; } uint64_t initial = 0; uint64_t max = 0; diff --git a/src/interp/binary-reader-interp.cc b/src/interp/binary-reader-interp.cc index ca6568b97..10ba15f78 100644 --- a/src/interp/binary-reader-interp.cc +++ b/src/interp/binary-reader-interp.cc @@ -771,6 +771,10 @@ Result BinaryReaderInterp::EndDataSegmentInitExpr(Index index) { CHECK_RESULT(validator_.OnDataSegmentInitExpr_Const(loc, ValueType::I32)); break; + case InitExprKind::I64: + CHECK_RESULT(validator_.OnDataSegmentInitExpr_Const(loc, ValueType::I64)); + break; + case InitExprKind::GlobalGet: CHECK_RESULT(validator_.OnDataSegmentInitExpr_GlobalGet( loc, Var(init_expr_.index_))); diff --git a/src/interp/interp.cc b/src/interp/interp.cc index 6fd391aca..cbcfbaf68 100644 --- a/src/interp/interp.cc +++ b/src/interp/interp.cc @@ -850,7 +850,9 @@ Instance::Ptr Instance::Instantiate(Store& store, if (desc.mode == SegmentMode::Active) { Result result; Memory::Ptr memory{store, inst->memories_[desc.memory_index]}; - u32 offset = inst->ResolveInitExpr(store, desc.offset).Get(); + Value offset_op = inst->ResolveInitExpr(store, desc.offset); + u64 offset = memory->type().limits.is_64 ? offset_op.Get() + : offset_op.Get(); if (pass == Check) { result = memory->IsValidAccess(offset, 0, segment.size()) ? Result::Ok @@ -864,8 +866,9 @@ Instance::Ptr Instance::Instantiate(Store& store, *out_trap = Trap::New( store, StringPrintf("out of bounds memory access: data segment is " - "out of bounds: [%u, %" PRIu64 ") >= max value %" - PRIu64, offset, u64{offset} + segment.size(), + "out of bounds: [%" PRIu64 ", %" PRIu64 + ") >= max value %" + PRIu64, offset, offset + segment.size(), memory->ByteSize())); return {}; } diff --git a/src/shared-validator.cc b/src/shared-validator.cc index 354c8e931..9c62bf0f9 100644 --- a/src/shared-validator.cc +++ b/src/shared-validator.cc @@ -371,7 +371,9 @@ Result SharedValidator::OnDataSegment(const Location& loc, Result SharedValidator::OnDataSegmentInitExpr_Const(const Location& loc, Type type) { - return CheckType(loc, type, Type::I32, "data segment offset"); + auto required = + memories_.empty() ? Type(Type::I32) : memories_[0].limits.IndexType(); + return CheckType(loc, type, required, "data segment offset"); } Result SharedValidator::OnDataSegmentInitExpr_GlobalGet(const Location& loc, @@ -385,14 +387,16 @@ Result SharedValidator::OnDataSegmentInitExpr_GlobalGet(const Location& loc, loc, "initializer expression cannot reference a mutable global"); } - result |= CheckType(loc, ref_global.type, Type::I32, "data segment offset"); + auto required = + memories_.empty() ? Type(Type::I32) : memories_[0].limits.IndexType(); + result |= CheckType(loc, ref_global.type, required, "data segment offset"); return result; } Result SharedValidator::OnDataSegmentInitExpr_Other(const Location& loc) { return PrintError(loc, "invalid data segment offset, must be a constant " - "expression; either i32.const or " + "expression; either iXX.const or " "global.get."); } diff --git a/src/type-checker.cc b/src/type-checker.cc index df302a1da..1826e1d2c 100644 --- a/src/type-checker.cc +++ b/src/type-checker.cc @@ -697,7 +697,7 @@ Result TypeChecker::OnMemoryInit(uint32_t segment, const Limits& limits) { } Result TypeChecker::OnMemorySize(const Limits& limits) { - PushType(limits.is_64 ? Type::I64 : Type::I32); + PushType(limits.IndexType()); return Result::Ok; } diff --git a/src/wast-parser.cc b/src/wast-parser.cc index 9bff0156f..fde32e50f 100644 --- a/src/wast-parser.cc +++ b/src/wast-parser.cc @@ -1484,7 +1484,8 @@ Result WastParser::ParseMemoryModuleField(Module* module) { auto data_segment_field = MakeUnique(loc); DataSegment& data_segment = data_segment_field->data_segment; data_segment.memory_var = Var(module->memories.size()); - data_segment.offset.push_back(MakeUnique(Const::I32(0))); + data_segment.offset.push_back(MakeUnique( + field->memory.page_limits.is_64 ? Const::I64(0) : Const::I32(0))); data_segment.offset.back().loc = loc; ParseTextListOpt(&data_segment.data); EXPECT(Rpar); diff --git a/test/interp/load64.txt b/test/interp/load64.txt index 387edb819..50765f209 100644 --- a/test/interp/load64.txt +++ b/test/interp/load64.txt @@ -2,10 +2,10 @@ ;;; ARGS: --enable-memory64 (module (memory i64 1) - (data (i32.const 0) "\ff\ff\ff\ff") - (data (i32.const 4) "\00\00\ce\41") - (data (i32.const 8) "\00\00\00\00\00\ff\8f\40") - (data (i32.const 16) "\ff\ff\ff\ff\ff\ff\ff\ff") + (data (i64.const 0) "\ff\ff\ff\ff") + (data (i64.const 4) "\00\00\ce\41") + (data (i64.const 8) "\00\00\00\00\00\ff\8f\40") + (data (i64.const 16) "\ff\ff\ff\ff\ff\ff\ff\ff") (func (export "i32_load8_s") (result i32) i64.const 0 diff --git a/test/regress/regress-27.txt b/test/regress/regress-27.txt index f984b1aba..594e4162f 100644 --- a/test/regress/regress-27.txt +++ b/test/regress/regress-27.txt @@ -14,6 +14,6 @@ section(DATA) { data[str("test")] } (;; STDERR ;;; -error: invalid data segment offset, must be a constant expression; either i32.const or global.get. +error: invalid data segment offset, must be a constant expression; either iXX.const or global.get. 0000012: error: EndDataSegmentInitExpr callback failed ;;; STDERR ;;) diff --git a/test/spec/data.txt b/test/spec/data.txt index ced8756e1..82c7d9f96 100644 --- a/test/spec/data.txt +++ b/test/spec/data.txt @@ -43,7 +43,7 @@ out/test/spec/data.wast:337: assert_invalid passed: out/test/spec/data.wast:355: assert_invalid passed: 0000013: error: expected i32 init_expr out/test/spec/data.wast:363: assert_invalid passed: - error: invalid data segment offset, must be a constant expression; either i32.const or global.get. + error: invalid data segment offset, must be a constant expression; either iXX.const or global.get. 0000012: error: EndDataSegmentInitExpr callback failed out/test/spec/data.wast:371: assert_invalid passed: 0000014: error: expected END opcode after initializer expression diff --git a/third_party/testsuite b/third_party/testsuite index 9994915e0..01efde810 160000 --- a/third_party/testsuite +++ b/third_party/testsuite @@ -1 +1 @@ -Subproject commit 9994915e0cca8b42a16c577e4c85491822367dde +Subproject commit 01efde81028c5b0d099eb836645a2dc5e7755449