| 410 | // ── Memory instructions ─────────────────────────────────────────────────────── |
| 411 | |
| 412 | WasmInstr ParseContext::parse_memoryinstr(const std::string& name) { |
| 413 | if (name == "data.drop") return Instr::Data_drop(parse_dataidx()); |
| 414 | |
| 415 | if (name == "memory.copy") { |
| 416 | index_t dst = 0, src = 0; |
| 417 | if (tok_.peek().type == TokenType::Integer || tok_.peek().type == TokenType::Id) { |
| 418 | dst = parse_memidx(); src = parse_memidx(); |
| 419 | } |
| 420 | return Instr::Memory_copy(dst, src); |
| 421 | } |
| 422 | |
| 423 | // memory.{size,grow,fill,init}: optional memidx |
| 424 | if (name.starts_with("memory.")) { |
| 425 | index_t memidx = 0; |
| 426 | if (tok_.peek().type == TokenType::Integer || tok_.peek().type == TokenType::Id) |
| 427 | memidx = parse_memidx(); |
| 428 | if (name == "memory.size") return Instr::Memory_size(memidx); |
| 429 | if (name == "memory.grow") return Instr::Memory_grow(memidx); |
| 430 | if (name == "memory.fill") return Instr::Memory_fill(memidx); |
| 431 | if (name == "memory.init") { |
| 432 | // memidx? dataidx |
| 433 | if (tok_.peek().type == TokenType::Integer || tok_.peek().type == TokenType::Id) { |
| 434 | // had memidx, now get dataidx |
| 435 | return Instr::Memory_init(memidx, parse_dataidx()); |
| 436 | } |
| 437 | // only one index: it was dataidx |
| 438 | return Instr::Memory_init(0, memidx); |
| 439 | } |
| 440 | } |
| 441 | |
| 442 | // Load/store: optional memidx then memarg |
| 443 | index_t memidx = 0; |
| 444 | if (tok_.peek().type == TokenType::Integer || tok_.peek().type == TokenType::Id) { |
| 445 | // Could be memidx before offset=, but memidx is only present if followed by another token |
| 446 | // that is also an index or the next token is offset=/align= |
| 447 | // Simple heuristic: if the next token is an integer/id and no offset/align follows immediately |
| 448 | // For now: if tok[0] is integer/id and tok[1] is integer/id/offset/align/RParen/EOF → tok[0] is memidx |
| 449 | // Actually the WAT spec says memidx is optional and only present in multi-memory proposal. |
| 450 | // For simplicity: just try to parse memidx if there's an integer/id before offset= or end |
| 451 | // The tokenizer returns Offset/Align for those, so any integer/id here would be memidx. |
| 452 | // But we need to be careful — if there's only one integer and it could be the value of |
| 453 | // i32.const etc. that came before... no, those are not in memoryinstr context. |
| 454 | // The only integer/id that can appear before offset= in a memory instr is memidx. |
| 455 | memidx = parse_u32(); |
| 456 | // Check if there's actually more (otherwise treat as... hmm) |
| 457 | // Actually per grammar: memidx? is only for multi-memory. In practice single-memory |
| 458 | // modules don't use it. But we should support it. |
| 459 | // The ambiguity is: if there's ONE integer before offset/align/end, is it memidx or not? |
| 460 | // In single-memory WAT: no memidx appears. In multi-memory: memidx appears. |
| 461 | // The grammar says "memidx?" so it's optional. Since an integer here is always memidx |
| 462 | // (there's nothing else an integer would be), we're fine. |
| 463 | } |
| 464 | auto [offset, align] = parse_memarg(); |
| 465 | |
| 466 | // Default alignment based on instruction width |
| 467 | if (!align.has_value()) { |
| 468 | if (name.ends_with("8") || name.ends_with("8_s") || name.ends_with("8_u")) align = 0; |
| 469 | else if (name.ends_with("16") || name.ends_with("16_s") || name.ends_with("16_u")) align = 1; |
nothing calls this directly
no test coverage detected