Skip to content

Commit

Permalink
[Loader] Rebase and extend the serializer for typed-function reference
Browse files Browse the repository at this point in the history
proposal.

Signed-off-by: YiYing He <[email protected]>
  • Loading branch information
q82419 committed Oct 30, 2023
1 parent 3571602 commit 5aafbbe
Show file tree
Hide file tree
Showing 14 changed files with 235 additions and 256 deletions.
1 change: 0 additions & 1 deletion include/ast/type.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class Limit {
Type = LimitType::HasMinMax;
}
}
Limit(const Limit &L) noexcept : Type(L.Type), Min(L.Min), Max(L.Max) {}

/// Getter and setter of limit mode.
bool hasMax() const noexcept {
Expand Down
106 changes: 12 additions & 94 deletions include/common/configure.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

#include "common/enum_ast.hpp"
#include "common/enum_configure.hpp"
#include "common/enum_types.hpp"
#include "common/errcode.h"

#include <atomic>
#include <bitset>
#include <cstdint>
#include <initializer_list>
#include <mutex>
#include <optional>
#include <shared_mutex>
#include <unordered_set>

Expand Down Expand Up @@ -271,95 +271,19 @@ class Configure {
return StatisticsConf;
}

class ProposalErrCode {
public:
ProposalErrCode(const ErrCode::Value E) noexcept : Code(E) {}
ProposalErrCode(const ErrCode::Value E, const Proposal P) noexcept
: Code(E), IsNeedProposal(true), Prop(P) {}

bool isNeedProposal() const noexcept { return IsNeedProposal; }
ErrCode::Value getErrCode() const noexcept { return Code; }
Proposal getNeedProposal() const noexcept { return Prop; }

private:
ErrCode::Value Code = ErrCode::Value::Success;
bool IsNeedProposal = false;
Proposal Prop = Proposal::Max;
};

/// Helper function of checking the valid value types.
Expected<void, ProposalErrCode>
checkValTypeProposals(ValType VType) const noexcept {
switch (VType) {
case ValType::I32:
case ValType::I64:
case ValType::F32:
case ValType::F64:
return {};
case ValType::V128:
if (!hasProposal(Proposal::SIMD)) {
return Unexpected<ProposalErrCode>(
ProposalErrCode(ErrCode::Value::MalformedValType, Proposal::SIMD));
}
return {};
case ValType::FuncRef:
if (!hasProposal(Proposal::BulkMemoryOperations)) {
return Unexpected<ProposalErrCode>(ProposalErrCode(
ErrCode::Value::MalformedElemType, Proposal::BulkMemoryOperations));
}
[[fallthrough]];
case ValType::ExternRef:
if (!hasProposal(Proposal::ReferenceTypes)) {
return Unexpected<ProposalErrCode>(ProposalErrCode(
ErrCode::Value::MalformedElemType, Proposal::ReferenceTypes));
}
return {};
default:
return Unexpected<ProposalErrCode>(
ProposalErrCode(ErrCode::Value::MalformedValType));
}
}

/// Helper function of checking the valid reference types.
Expected<void, ProposalErrCode>
checkRefTypeProposals(RefType RType) const noexcept {
switch (RType) {
case RefType::ExternRef:
if (!hasProposal(Proposal::ReferenceTypes)) {
return Unexpected<ProposalErrCode>(ProposalErrCode(
ErrCode::Value::MalformedElemType, Proposal::ReferenceTypes));
}
[[fallthrough]];
case RefType::FuncRef:
return {};
default:
if (hasProposal(Proposal::ReferenceTypes)) {
return Unexpected<ProposalErrCode>(
ProposalErrCode(ErrCode::Value::MalformedRefType));
} else {
return Unexpected<ProposalErrCode>(
ProposalErrCode(ErrCode::Value::MalformedElemType));
}
}
}

/// Helper function of checking the proposal of instructions.
Expected<void, ProposalErrCode>
checkInstrProposals(OpCode Code) const noexcept {
std::optional<Proposal> isInstrNeedProposal(OpCode Code) const noexcept {
if (Code >= OpCode::I32__trunc_sat_f32_s &&
Code <= OpCode::I64__trunc_sat_f64_u) {
// These instructions are for NonTrapFloatToIntConversions proposal.
if (unlikely(!hasProposal(Proposal::NonTrapFloatToIntConversions))) {
return Unexpected<ProposalErrCode>(
ProposalErrCode(ErrCode::Value::IllegalOpCode,
Proposal::NonTrapFloatToIntConversions));
return Proposal::NonTrapFloatToIntConversions;
}
} else if (Code >= OpCode::I32__extend8_s &&
Code <= OpCode::I64__extend32_s) {
// These instructions are for SignExtensionOperators proposal.
if (unlikely(!hasProposal(Proposal::SignExtensionOperators))) {
return Unexpected<ProposalErrCode>(ProposalErrCode(
ErrCode::Value::IllegalOpCode, Proposal::SignExtensionOperators));
return Proposal::SignExtensionOperators;
}
} else if ((Code >= OpCode::Ref__null && Code <= OpCode::Ref__func) ||
(Code >= OpCode::Table__init && Code <= OpCode::Table__copy) ||
Expand All @@ -368,48 +292,42 @@ class Configure {
// proposal.
if (unlikely(!hasProposal(Proposal::ReferenceTypes)) &&
unlikely(!hasProposal(Proposal::BulkMemoryOperations))) {
return Unexpected<ProposalErrCode>(ProposalErrCode(
ErrCode::Value::IllegalOpCode, Proposal::ReferenceTypes));
return Proposal::ReferenceTypes;
}
} else if (Code == OpCode::Select_t ||
(Code >= OpCode::Table__get && Code <= OpCode::Table__set) ||
(Code >= OpCode::Table__grow && Code <= OpCode::Table__fill)) {
// These instructions are for ReferenceTypes proposal.
if (unlikely(!hasProposal(Proposal::ReferenceTypes))) {
return Unexpected<ProposalErrCode>(ProposalErrCode(
ErrCode::Value::IllegalOpCode, Proposal::ReferenceTypes));
return Proposal::ReferenceTypes;
}
} else if (Code >= OpCode::V128__load &&
Code <= OpCode::F64x2__convert_low_i32x4_u) {
// These instructions are for SIMD proposal.
if (!hasProposal(Proposal::SIMD)) {
return Unexpected<ProposalErrCode>(
ProposalErrCode(ErrCode::Value::IllegalOpCode, Proposal::SIMD));
return Proposal::SIMD;
}
} else if (Code == OpCode::Return_call ||
Code == OpCode::Return_call_indirect) {
// These instructions are for TailCall proposal.
if (!hasProposal(Proposal::TailCall)) {
return Unexpected<ProposalErrCode>(
ProposalErrCode(ErrCode::Value::IllegalOpCode, Proposal::TailCall));
return Proposal::TailCall;
}
} else if (Code >= OpCode::I32__atomic__load &&
Code <= OpCode::I64__atomic__rmw32__cmpxchg_u) {
// These instructions are for Thread proposal.
if (!hasProposal(Proposal::Threads)) {
return Unexpected<ProposalErrCode>(
ProposalErrCode(ErrCode::Value::IllegalOpCode, Proposal::Threads));
return Proposal::Threads;
}
} else if (Code == OpCode::Call_ref || Code == OpCode::Return_call_ref ||
Code == OpCode::Ref__as_non_null || Code == OpCode::Br_on_null ||
Code == OpCode::Br_on_non_null) {
// These instructions are for TypedFunctionReferences proposal.
if (!hasProposal(Proposal::FunctionReferences)) {
return Unexpected<ProposalErrCode>(ProposalErrCode(
ErrCode::Value::IllegalOpCode, Proposal::FunctionReferences));
return Proposal::FunctionReferences;
}
if (Code == OpCode::Return_call_ref && !hasProposal(Proposal::TailCall)) {
return Unexpected<ProposalErrCode>(
ProposalErrCode(ErrCode::Value::IllegalOpCode, Proposal::TailCall));
return Proposal::TailCall;
}
}
return {};
Expand Down
24 changes: 0 additions & 24 deletions include/loader/loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,30 +123,6 @@ class Loader {
spdlog::error(ErrInfo::InfoAST(Node));
return Unexpect(Code);
}
Expect<void> checkValTypeProposals(ValType VType, uint64_t Off,
ASTNodeAttr Node) const noexcept {
if (auto Res = Conf.checkValTypeProposals(VType); !Res) {
if (Res.error().isNeedProposal()) {
return logNeedProposal(Res.error().getErrCode(),
Res.error().getNeedProposal(), Off, Node);
} else {
return logLoadError(Res.error().getErrCode(), Off, Node);
}
}
return {};
}
Expect<void> checkRefTypeProposals(RefType RType, uint64_t Off,
ASTNodeAttr Node) const noexcept {
if (auto Res = Conf.checkRefTypeProposals(RType); !Res) {
if (Res.error().isNeedProposal()) {
return logNeedProposal(Res.error().getErrCode(),
Res.error().getNeedProposal(), Off, Node);
} else {
return logLoadError(Res.error().getErrCode(), Off, Node);
}
}
return {};
}
/// @}

/// \name Load AST Module functions
Expand Down
33 changes: 9 additions & 24 deletions include/loader/serialize.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include "ast/module.h"
#include "common/configure.h"
#include "common/errcode.h"
#include "common/errinfo.h"

#include <vector>
Expand Down Expand Up @@ -65,6 +66,8 @@ class Serializer {
private:
/// \name Serialize functions for the other nodes of AST.
/// @{
Expect<void> serializeSegment(const AST::TableSegment &Seg,
std::vector<uint8_t> &OutVec) const noexcept;
Expect<void> serializeSegment(const AST::GlobalSegment &Seg,
std::vector<uint8_t> &OutVec) const noexcept;
Expect<void> serializeSegment(const AST::ElementSegment &Seg,
Expand All @@ -77,6 +80,12 @@ class Serializer {
std::vector<uint8_t> &OutVec) const noexcept;
Expect<void> serializeDesc(const AST::ExportDesc &Desc,
std::vector<uint8_t> &OutVec) const noexcept;
Expect<void> serializeHeapType(const ValType &Type, ASTNodeAttr From,
std::vector<uint8_t> &OutVec) const noexcept;
Expect<void> serializeRefType(const ValType &Type, ASTNodeAttr From,
std::vector<uint8_t> &OutVec) const noexcept;
Expect<void> serializeValType(const ValType &Type, ASTNodeAttr From,
std::vector<uint8_t> &OutVec) const noexcept;
Expect<void> serializeLimit(const AST::Limit &Lim,
std::vector<uint8_t> &OutVec) const noexcept;
Expect<void> serializeType(const AST::FunctionType &Type,
Expand Down Expand Up @@ -109,30 +118,6 @@ class Serializer {
spdlog::error(ErrInfo::InfoAST(Node));
return Unexpect(Code);
}
Expect<void> checkValTypeProposals(ValType VType,
ASTNodeAttr Node) const noexcept {
if (auto Res = Conf.checkValTypeProposals(VType); !Res) {
if (Res.error().isNeedProposal()) {
return logNeedProposal(Res.error().getErrCode(),
Res.error().getNeedProposal(), Node);
} else {
return logSerializeError(Res.error().getErrCode(), Node);
}
}
return {};
}
Expect<void> checkRefTypeProposals(RefType RType,
ASTNodeAttr Node) const noexcept {
if (auto Res = Conf.checkRefTypeProposals(RType); !Res) {
if (Res.error().isNeedProposal()) {
return logNeedProposal(Res.error().getErrCode(),
Res.error().getNeedProposal(), Node);
} else {
return logSerializeError(Res.error().getErrCode(), Node);
}
}
return {};
}

template <typename NumType, size_t N>
void serializeUN(NumType Num, std::vector<uint8_t> &OutVec,
Expand Down
5 changes: 2 additions & 3 deletions lib/loader/ast/instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,8 @@ Expect<AST::InstrVec> Loader::loadInstrSeq(std::optional<uint64_t> SizeBound) {
}

// Check with proposals.
if (auto Res = Conf.checkInstrProposals(Code); !Res) {
return logNeedProposal(Res.error().getErrCode(),
Res.error().getNeedProposal(), Offset,
if (auto Res = Conf.isInstrNeedProposal(Code); unlikely(Res.has_value())) {
return logNeedProposal(ErrCode::Value::IllegalOpCode, Res.value(), Offset,
ASTNodeAttr::Instruction);
}

Expand Down
58 changes: 21 additions & 37 deletions lib/loader/serialize/serial_instruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,10 @@ Serializer::serializeInstruction(const AST::Instruction &Instr,
serializeOpCode(Instr.getOpCode(), OutVec);

// Check with proposals.
if (auto Res = Conf.checkInstrProposals(Instr.getOpCode()); !Res) {
spdlog::error(Res.error().getErrCode());
if (Res.error().isNeedProposal()) {
spdlog::error(ErrInfo::InfoProposal(Res.error().getNeedProposal()));
}
spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Instruction));
return Unexpect(Res.error().getErrCode());
if (auto Res = Conf.isInstrNeedProposal(Instr.getOpCode());
unlikely(Res.has_value())) {
return logNeedProposal(ErrCode::Value::IllegalOpCode, Res.value(),
ASTNodeAttr::Instruction);
}

switch (Instr.getOpCode()) {
Expand All @@ -65,32 +62,21 @@ Serializer::serializeInstruction(const AST::Instruction &Instr,
case OpCode::Block:
case OpCode::Loop:
case OpCode::If:
switch (Instr.getBlockType().TypeFlag) {
case BlockType::TypeEnum::Empty:
OutVec.push_back(0x40U);
break;

case BlockType::TypeEnum::ValType: {
auto VType = Instr.getBlockType().Data.Type;
if (auto Check = checkValTypeProposals(VType, ASTNodeAttr::Instruction);
unlikely(!Check)) {
return Unexpect(Check);
if (Instr.getBlockType().isEmpty()) {
OutVec.push_back(static_cast<uint8_t>(TypeCode::Epsilon));
} else if (Instr.getBlockType().isValType()) {
if (auto Res = serializeValType(Instr.getBlockType().getValType(),
ASTNodeAttr::Instruction, OutVec);
unlikely(!Res)) {
return Unexpect(Res);

Check warning on line 71 in lib/loader/serialize/serial_instruction.cpp

View check run for this annotation

Codecov / codecov/patch

lib/loader/serialize/serial_instruction.cpp#L69-L71

Added lines #L69 - L71 were not covered by tests
}
OutVec.push_back(static_cast<uint8_t>(VType));
break;
}

case BlockType::TypeEnum::TypeIdx:
} else {
if (unlikely(!Conf.hasProposal(Proposal::MultiValue))) {
return logNeedProposal(ErrCode::Value::MalformedValType,
Proposal::MultiValue, ASTNodeAttr::Instruction);

Check warning on line 76 in lib/loader/serialize/serial_instruction.cpp

View check run for this annotation

Codecov / codecov/patch

lib/loader/serialize/serial_instruction.cpp#L75-L76

Added lines #L75 - L76 were not covered by tests
}
serializeS33(static_cast<int64_t>(Instr.getBlockType().Data.Idx), OutVec);
break;

default:
return logSerializeError(ErrCode::Value::Unreachable,
ASTNodeAttr::Instruction);
serializeS33(static_cast<int64_t>(Instr.getBlockType().getTypeIndex()),

Check warning on line 78 in lib/loader/serialize/serial_instruction.cpp

View check run for this annotation

Codecov / codecov/patch

lib/loader/serialize/serial_instruction.cpp#L78

Added line #L78 was not covered by tests
OutVec);
}
return {};

Expand Down Expand Up @@ -129,12 +115,11 @@ Serializer::serializeInstruction(const AST::Instruction &Instr,

// Reference Instructions.
case OpCode::Ref__null:
if (auto Check =
checkRefTypeProposals(Instr.getRefType(), ASTNodeAttr::Instruction);
unlikely(!Check)) {
return Unexpect(Check);
if (auto Res = serializeRefType(Instr.getValType(),
ASTNodeAttr::Instruction, OutVec);
unlikely(!Res)) {
return Unexpect(Res);
}
OutVec.push_back(static_cast<uint8_t>(Instr.getRefType()));
return {};
case OpCode::Ref__is_null:
return {};
Expand All @@ -150,11 +135,10 @@ Serializer::serializeInstruction(const AST::Instruction &Instr,
uint32_t VecCnt = static_cast<uint32_t>(Instr.getValTypeList().size());
serializeU32(VecCnt, OutVec);
for (auto &VType : Instr.getValTypeList()) {
if (auto Check = checkValTypeProposals(VType, ASTNodeAttr::Instruction);
unlikely(!Check)) {
return Unexpect(Check);
if (auto Res = serializeValType(VType, ASTNodeAttr::Instruction, OutVec);
unlikely(!Res)) {
return Unexpect(Res);
}
OutVec.push_back(static_cast<uint8_t>(VType));
}
return {};
}
Expand Down
4 changes: 2 additions & 2 deletions lib/loader/serialize/serial_section.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ Expect<std::vector<uint8_t>>
Serializer::serializeSection(const AST::TableSection &Sec) const noexcept {
// Table section: 0x04 + size:u32 + content:vec(tabletype).
return serializeSectionContent(
Sec, 0x04U, [=](const AST::TableType &R, std::vector<uint8_t> &V) {
return serializeType(R, V);
Sec, 0x04U, [=](const AST::TableSegment &R, std::vector<uint8_t> &V) {
return serializeSegment(R, V);
});
}

Expand Down

0 comments on commit 5aafbbe

Please sign in to comment.