Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/action/bytecode_visitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,45 @@ template <typename IRBuilder> class WASMByteCodeVisitor {
handleIntExtend<WASMType::I64, WASMType::I32, true>();
break;

case common::WASM_PREFIX_FC: { // Bulk memory operations prefix
uint32_t SubOpcode;
Ip = readSafeLEBNumber(Ip, SubOpcode);
// Skip operands based on sub-opcode
switch (SubOpcode) {
case common::FC_MEMORY_INIT: // memory.init: dataidx(LEB) + memidx(1
// byte)
Ip = utils::skipLEBNumber<uint32_t>(Ip, IpEnd);
Ip++; // skip memidx
break;
case common::FC_DATA_DROP: // data.drop: dataidx(LEB)
Ip = utils::skipLEBNumber<uint32_t>(Ip, IpEnd);
break;
case common::FC_MEMORY_COPY: // memory.copy: 2 bytes
Ip += 2;
break;
case common::FC_MEMORY_FILL: // memory.fill: 1 byte
Ip++;
break;
case common::FC_TABLE_INIT: // table.init: elemidx(LEB) + tableidx(LEB)
Ip = utils::skipLEBNumber<uint32_t>(Ip, IpEnd);
Ip = utils::skipLEBNumber<uint32_t>(Ip, IpEnd);
break;
case common::FC_ELEM_DROP: // elem.drop: elemidx(LEB)
Ip = utils::skipLEBNumber<uint32_t>(Ip, IpEnd);
break;
case common::FC_TABLE_COPY: // table.copy: dst_tableidx(LEB) +
// src_tableidx(LEB)
Ip = utils::skipLEBNumber<uint32_t>(Ip, IpEnd);
Ip = utils::skipLEBNumber<uint32_t>(Ip, IpEnd);
break;
default:
break;
}
throw getErrorWithExtraMessage(
ErrorCode::UnsupportedOpcode,
"bulk memory operations not supported in JIT mode");
}

default:
throw getErrorWithExtraMessage(ErrorCode::UnsupportedOpcode,
std::to_string(Opcode));
Expand Down
107 changes: 107 additions & 0 deletions src/action/function_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,113 @@ void FunctionLoader::load() {
case I64_EXTEND32_S:
popAndPushValueType(1, WASMType::I64, WASMType::I64);
break;
case WASM_PREFIX_FC: {
// Multi-byte opcode prefix for bulk memory operations
uint32_t SubOpcode = readU32();
switch (SubOpcode) {
case FC_MEMORY_INIT: { // memory.init: dataidx + 0x00
uint32_t DataIdx = readU32();
uint8_t MemIdx = to_underlying(readByte());
if (MemIdx != 0x00) {
throw getError(ErrorCode::ZeroFlagExpected);
}
if (Mod.DataCount == -1u) {
throw getError(ErrorCode::UnknownDataSegment);
}
if (!Mod.isValidDataSegment(DataIdx)) {
throw getError(ErrorCode::UnknownDataSegment);
}
if (!hasMemory()) {
throw getError(ErrorCode::UnknownMemory);
}
popValueType(WASMType::I32); // n
popValueType(WASMType::I32); // s
popValueType(WASMType::I32); // d
FuncCodeEntry.Stats |= Module::SF_memory;
break;
}
case FC_DATA_DROP: { // data.drop: dataidx
uint32_t DataIdx = readU32();
if (Mod.DataCount == -1u) {
throw getError(ErrorCode::UnknownDataSegment);
}
if (!Mod.isValidDataSegment(DataIdx)) {
throw getError(ErrorCode::UnknownDataSegment);
}
break;
}
case FC_MEMORY_COPY: { // memory.copy: 0x00 0x00
uint8_t DstMemIdx = to_underlying(readByte());
uint8_t SrcMemIdx = to_underlying(readByte());
if (DstMemIdx != 0x00 || SrcMemIdx != 0x00) {
throw getError(ErrorCode::ZeroFlagExpected);
}
if (!hasMemory()) {
throw getError(ErrorCode::UnknownMemory);
}
popValueType(WASMType::I32); // n
popValueType(WASMType::I32); // s
popValueType(WASMType::I32); // d
FuncCodeEntry.Stats |= Module::SF_memory;
break;
}
case FC_MEMORY_FILL: { // memory.fill: 0x00
uint8_t MemIdx = to_underlying(readByte());
if (MemIdx != 0x00) {
throw getError(ErrorCode::ZeroFlagExpected);
}
if (!hasMemory()) {
throw getError(ErrorCode::UnknownMemory);
}
popValueType(WASMType::I32); // n
popValueType(WASMType::I32); // val
popValueType(WASMType::I32); // d
FuncCodeEntry.Stats |= Module::SF_memory;
break;
}
case FC_TABLE_INIT: { // table.init: elemidx + tableidx
uint32_t ElemIdx = readU32();
uint32_t TableIdx = readU32();
if (!Mod.isValidElemSegment(ElemIdx)) {
throw getError(ErrorCode::UnknownElemSegment);
}
if (!Mod.isValidTable(TableIdx)) {
throw getError(ErrorCode::UnknownTable);
}
popValueType(WASMType::I32); // n
popValueType(WASMType::I32); // s
popValueType(WASMType::I32); // d
FuncCodeEntry.Stats |= Module::SF_table;
break;
}
case FC_ELEM_DROP: { // elem.drop: elemidx
uint32_t ElemIdx = readU32();
if (!Mod.isValidElemSegment(ElemIdx)) {
throw getError(ErrorCode::UnknownElemSegment);
}
break;
}
case FC_TABLE_COPY: { // table.copy: dst_tableidx + src_tableidx
uint32_t DstTableIdx = readU32();
uint32_t SrcTableIdx = readU32();
if (!Mod.isValidTable(DstTableIdx)) {
throw getError(ErrorCode::UnknownTable);
}
if (!Mod.isValidTable(SrcTableIdx)) {
throw getError(ErrorCode::UnknownTable);
}
popValueType(WASMType::I32); // n
popValueType(WASMType::I32); // s
popValueType(WASMType::I32); // d
FuncCodeEntry.Stats |= Module::SF_table;
break;
}
default:
throw getErrorWithExtraMessage(ErrorCode::UnsupportedOpcode,
"0xFC " + std::to_string(SubOpcode));
}
break;
}
case I32_LOAD:
case I64_LOAD:
case F32_LOAD:
Expand Down
26 changes: 26 additions & 0 deletions src/action/instantiator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ void Instantiator::instantiateTables(Instance &Inst) {

for (uint32_t I = 0; I < Mod.NumElementSegments; ++I) {
const auto &Element = Mod.ElementTable[I];
if (Element.Mode != 0)
continue; // skip non-active segments
TableInstance &TableInst = Inst.Tables[Element.TableIdx];
uint32_t Offset = 0;
if (Element.InitExprKind == GET_GLOBAL) {
Expand Down Expand Up @@ -209,6 +211,8 @@ void Instantiator::initMemoryByDataSegments(Instance &Inst) {
const Module *Mod = Inst.Mod;
for (uint32_t I = 0; I < Mod->NumDataSegments; ++I) {
const auto &DataSeg = Mod->DataTable[I];
if (DataSeg.Mode != 0)
continue; // skip non-active segments
uint32_t MemIdx = DataSeg.MemIdx;
// should checked if MemIndex is valid in loader
MemoryInstance &MemInst = Inst.Memories[MemIdx];
Expand Down Expand Up @@ -349,6 +353,28 @@ void Instantiator::instantiate(Instance &Inst) {

instantiateMemories(Inst);

// Initialize dropped segment tracking arrays
if (Mod.NumDataSegments > 0) {
Inst.DroppedDataSegments =
(bool *)Inst.allocateZeros(sizeof(bool) * Mod.NumDataSegments);
// Mark active data segments as dropped after instantiation (per spec)
for (uint32_t I = 0; I < Mod.NumDataSegments; ++I) {
if (Mod.DataTable[I].Mode == 0) {
Inst.DroppedDataSegments[I] = true;
}
}
}
if (Mod.NumElementSegments > 0) {
Inst.DroppedElemSegments =
(bool *)Inst.allocateZeros(sizeof(bool) * Mod.NumElementSegments);
// Mark active element segments as dropped after instantiation (per spec)
for (uint32_t I = 0; I < Mod.NumElementSegments; ++I) {
if (Mod.ElementTable[I].Mode == 0) {
Inst.DroppedElemSegments[I] = true;
}
}
}

#ifdef ZEN_ENABLE_BUILTIN_WASI
if (!Inst.getRuntime()->getConfig().DisableWASI) {
instantiateWasi(Inst);
Expand Down
169 changes: 167 additions & 2 deletions src/action/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,39 @@ class BaseInterpreterImpl {
case I64_EXTEND16_S:
case I64_EXTEND32_S:
break;
case WASM_PREFIX_FC: { // Bulk memory operations prefix
uint32_t SubOpcode = 0;
Ptr = readSafeLEBNumber(Ptr, SubOpcode);
switch (SubOpcode) {
case FC_MEMORY_INIT: // memory.init: dataidx(LEB) + memidx(1 byte)
Ptr = skipLEBNumber<uint32_t>(Ptr, End);
Ptr++; // skip memidx
break;
case FC_DATA_DROP: // data.drop: dataidx(LEB)
Ptr = skipLEBNumber<uint32_t>(Ptr, End);
break;
case FC_MEMORY_COPY: // memory.copy: 2 bytes
Ptr += 2;
break;
case FC_MEMORY_FILL: // memory.fill: 1 byte
Ptr++;
break;
case FC_TABLE_INIT: // table.init: elemidx(LEB) + tableidx(LEB)
Ptr = skipLEBNumber<uint32_t>(Ptr, End);
Ptr = skipLEBNumber<uint32_t>(Ptr, End);
break;
case FC_ELEM_DROP: // elem.drop: elemidx(LEB)
Ptr = skipLEBNumber<uint32_t>(Ptr, End);
break;
case FC_TABLE_COPY: // table.copy: dst_tableidx(LEB) + src_tableidx(LEB)
Ptr = skipLEBNumber<uint32_t>(Ptr, End);
Ptr = skipLEBNumber<uint32_t>(Ptr, End);
break;
default:
break;
}
break;
}
case I32_LOAD:
case I64_LOAD:
case F32_LOAD:
Expand Down Expand Up @@ -2093,8 +2126,140 @@ void BaseInterpreterImpl::interpret() {
BREAK;
}
DEFAULT : {
ZEN_LOG_ERROR("munimplemented opcode: 0x%x", Opcode);
ZEN_ASSERT_TODO();
if (Opcode == WASM_PREFIX_FC) {
// Bulk memory operations prefix
uint32_t SubOpcode = 0;
Ip = readSafeLEBNumber(Ip, SubOpcode);
switch (SubOpcode) {
case FC_MEMORY_INIT: { // memory.init
uint32_t DataIdx = 0;
Ip = readSafeLEBNumber(Ip, DataIdx);
Ip++; // skip memidx (0x00)
uint32_t N = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t S = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t D = Frame->valuePop<uint32_t>(ValStackPtr);
const DataEntry *DataSeg = Mod->getDataEntry(DataIdx);
bool Dropped = ModInst->isDataSegmentDropped(DataIdx);
if (Dropped) {
if (N == 0 && S == 0) {
// Zero-length init with offset 0 on dropped segment is OK
// but still need to check D <= MemSize
if ((uint64_t)D > LinearMemSize) {
throw getError(ErrorCode::OutOfBoundsMemory);
}
BREAK;
}
throw getError(ErrorCode::OutOfBoundsMemory);
}
if ((uint64_t)S + (uint64_t)N > (uint64_t)DataSeg->Size) {
throw getError(ErrorCode::OutOfBoundsMemory);
}
if ((uint64_t)D + (uint64_t)N > LinearMemSize) {
throw getError(ErrorCode::OutOfBoundsMemory);
}
if (N > 0) {
std::memcpy(Memory->MemBase + D,
Mod->getWASMBytecode() + DataSeg->Offset + S, N);
}
BREAK;
}
case FC_DATA_DROP: { // data.drop
uint32_t DataIdx = 0;
Ip = readSafeLEBNumber(Ip, DataIdx);
ModInst->dropDataSegment(DataIdx);
BREAK;
}
case FC_MEMORY_COPY: { // memory.copy
Ip += 2; // skip dst_memidx and src_memidx (both 0x00)
uint32_t N = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t S = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t D = Frame->valuePop<uint32_t>(ValStackPtr);
if ((uint64_t)S + (uint64_t)N > LinearMemSize ||
(uint64_t)D + (uint64_t)N > LinearMemSize) {
throw getError(ErrorCode::OutOfBoundsMemory);
}
if (N > 0) {
std::memmove(Memory->MemBase + D, Memory->MemBase + S, N);
}
BREAK;
}
case FC_MEMORY_FILL: { // memory.fill
Ip++; // skip memidx (0x00)
uint32_t N = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t Val = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t D = Frame->valuePop<uint32_t>(ValStackPtr);
if ((uint64_t)D + (uint64_t)N > LinearMemSize) {
throw getError(ErrorCode::OutOfBoundsMemory);
}
if (N > 0) {
std::memset(Memory->MemBase + D, (uint8_t)Val, N);
}
BREAK;
}
case FC_TABLE_INIT: { // table.init
uint32_t ElemIdx = 0, TableIdx = 0;
Ip = readSafeLEBNumber(Ip, ElemIdx);
Ip = readSafeLEBNumber(Ip, TableIdx);
uint32_t N = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t S = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t D = Frame->valuePop<uint32_t>(ValStackPtr);
const ElemEntry *ElemSeg = Mod->getElemEntry(ElemIdx);
TableInstance *Table = ModInst->getTableInst(TableIdx);
bool Dropped = ModInst->isElemSegmentDropped(ElemIdx);
if (Dropped) {
if (N == 0 && S == 0) {
if (D > Table->CurSize) {
throw getError(ErrorCode::OutOfBoundsTable);
}
BREAK;
}
throw getError(ErrorCode::OutOfBoundsTable);
}
if ((uint64_t)S + (uint64_t)N > (uint64_t)ElemSeg->NumFuncIdxs) {
throw getError(ErrorCode::OutOfBoundsTable);
}
if ((uint64_t)D + (uint64_t)N > (uint64_t)Table->CurSize) {
throw getError(ErrorCode::OutOfBoundsTable);
}
if (N > 0) {
std::memcpy(Table->Elements + D, ElemSeg->FuncIdxs + S,
N * sizeof(uint32_t));
}
BREAK;
}
case FC_ELEM_DROP: { // elem.drop
uint32_t ElemIdx = 0;
Ip = readSafeLEBNumber(Ip, ElemIdx);
ModInst->dropElemSegment(ElemIdx);
BREAK;
}
case FC_TABLE_COPY: { // table.copy
uint32_t DstTableIdx = 0, SrcTableIdx = 0;
Ip = readSafeLEBNumber(Ip, DstTableIdx);
Ip = readSafeLEBNumber(Ip, SrcTableIdx);
uint32_t N = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t S = Frame->valuePop<uint32_t>(ValStackPtr);
uint32_t D = Frame->valuePop<uint32_t>(ValStackPtr);
TableInstance *DstTable = ModInst->getTableInst(DstTableIdx);
TableInstance *SrcTable = ModInst->getTableInst(SrcTableIdx);
if ((uint64_t)S + (uint64_t)N > (uint64_t)SrcTable->CurSize ||
(uint64_t)D + (uint64_t)N > (uint64_t)DstTable->CurSize) {
throw getError(ErrorCode::OutOfBoundsTable);
}
if (N > 0) {
std::memmove(DstTable->Elements + D, SrcTable->Elements + S,
N * sizeof(uint32_t));
}
BREAK;
}
default:
ZEN_LOG_ERROR("unimplemented 0xFC sub-opcode: 0x%x", SubOpcode);
ZEN_ASSERT_TODO();
}
} else {
ZEN_LOG_ERROR("munimplemented opcode: 0x%x", Opcode);
ZEN_ASSERT_TODO();
}
}
}
// TODO: write back ValueStackPtr, Ip, CtrlStackPtr to Frame
Expand Down
Loading
Loading