Changeset 270855 in webkit


Ignore:
Timestamp:
Dec 15, 2020 11:15:33 AM (19 months ago)
Author:
commit-queue@webkit.org
Message:

[WASM-References] Add support for memory.fill
https://bugs.webkit.org/show_bug.cgi?id=219848

Patch by Dmitry Bezhetskov <dbezhetskov> on 2020-12-15
Reviewed by Yusuke Suzuki.

JSTests:

Added spec tests and unreachable tests for memory.fill.

  • wasm.yaml:
  • wasm/references-spec-tests/memory_fill.wast.js: Added.
  • wasm/references/memory_fill_shared.js: Added.

(async test):

  • wasm/references/table_instructions_parse_unreachable.js:

(invalidMemoryFillUnreachable):

  • wasm/wasm.json:

Source/JavaScriptCore:

Add support for memory.fill from ref-types spec.
memory.fill sets all bytes in a memory region to a given byte:
https://webassembly.github.io/reference-types/core/syntax/instructions.html#memory-instructions.

  • bytecode/BytecodeList.rb:
  • llint/WebAssembly.asm:
  • wasm/WasmAirIRGenerator.cpp:

(JSC::Wasm::AirIRGenerator::addMemoryFill):

  • wasm/WasmB3IRGenerator.cpp:

(JSC::Wasm::B3IRGenerator::addMemoryFill):

  • wasm/WasmFunctionParser.h:

(JSC::Wasm::FunctionParser<Context>::parseMemoryFillImmediate):
(JSC::Wasm::FunctionParser<Context>::parseExpression):
(JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):

  • wasm/WasmLLIntGenerator.cpp:

(JSC::Wasm::LLIntGenerator::addMemoryFill):

  • wasm/WasmMemory.cpp:

(JSC::Wasm::Memory::fill):
(JSC::Wasm::Memory::doMemoryFill):

  • wasm/WasmMemory.h:
  • wasm/WasmOperations.cpp:

(JSC::Wasm::JSC_DEFINE_JIT_OPERATION):

  • wasm/WasmOperations.h:
  • wasm/WasmSlowPaths.cpp:

(JSC::LLInt::WASM_SLOW_PATH_DECL):

  • wasm/WasmSlowPaths.h:
  • wasm/wasm.json:
Location:
trunk
Files:
2 added
19 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r270827 r270855  
     12020-12-15  Dmitry Bezhetskov  <dbezhetskov@igalia.com>
     2
     3        [WASM-References] Add support for memory.fill
     4        https://bugs.webkit.org/show_bug.cgi?id=219848
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        Added spec tests and unreachable tests for memory.fill.
     9
     10        * wasm.yaml:
     11        * wasm/references-spec-tests/memory_fill.wast.js: Added.
     12        * wasm/references/memory_fill_shared.js: Added.
     13        (async test):
     14        * wasm/references/table_instructions_parse_unreachable.js:
     15        (invalidMemoryFillUnreachable):
     16        * wasm/wasm.json:
     17
    1182020-12-15  Dmitry Bezhetskov  <dbezhetskov@igalia.com>
    219
  • trunk/JSTests/wasm.yaml

    r270827 r270855  
    5757- path: wasm/references-spec-tests/select.wast.js
    5858  cmd: runWebAssemblyReferenceSpecTest :normal
     59- path: wasm/references-spec-tests/memory_fill.wast.js
     60  cmd: runWebAssemblyReferenceSpecTest :normal
    5961
    6062- path: wasm/multi-value-spec-tests/block.wast.js
  • trunk/JSTests/wasm/references/table_instructions_parse_unreachable.js

    r270827 r270855  
    254254}
    255255
     256function validMemoryFillUnreachable() {
     257  /*
     258  (module
     259    (memory $mem0 1)
     260    (func (export "run")
     261      (return)
     262      (memory.fill (i32.const 0) (i32.const 11) (i32.const 3))
     263    )
     264  )
     265  */
     266  let instance = new WebAssembly.Instance(module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x04\x01\x60\x00\x00\x03\x02\x01\x00\x05\x03\x01\x00\x01\x07\x07\x01\x03\x72\x75\x6e\x00\x00\x0a\x0e\x01\x0c\x00\x0f\x41\x00\x41\x0b\x41\x03\xfc\x0b\x00\x0b"));
     267  instance.exports.run();
     268}
     269
     270function invalidMemoryFillUnreachable() {
     271  /*
     272  (module
     273    (memory $mem0 1)
     274    (func (export "run")
     275      (return)
     276      (memory.fill (unused = 1) (i32.const 0) (i32.const 11) (i32.const 3))
     277    )
     278  )
     279  */
     280  assert.throws(() => module("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x04\x01\x60\x00\x00\x03\x02\x01\x00\x05\x03\x01\x00\x01\x07\x07\x01\x03\x72\x75\x6e\x00\x00\x0a\x0e\x01\x0c\x00\x0f\x41\x00\x41\x0b\x41\x03\xfc\x0b\x01\x0b"),
     281  WebAssembly.CompileError,
     282  "WebAssembly.Module doesn't parse at byte 11: auxiliary byte for memory.fill should be zero, but got 1, in function at index 0 (evaluating 'new WebAssembly.Module(buffer)')");
     283}
     284
    256285validTableInitUnreachable();
    257286invalidTableInitUnreachable();
     
    274303validAnnotatedSelectUnreachable();
    275304invalidAnnotatedSelectUnreachable();
     305
     306validMemoryFillUnreachable();
     307invalidMemoryFillUnreachable();
  • trunk/JSTests/wasm/wasm.json

    r270827 r270855  
    7979        "table.fill":          { "category": "exttable",   "value":  252, "return": [],                              "parameter": ["i32", "externref", "i32"],    "immediate": [{"name": "table_index",    "type": "varuint32"}],                                             "description": "fill entries [i,i+n) with the given value", "extendedOp": 17 },
    8080        "table.copy":          { "category": "exttable",   "value":  252, "return": [],                              "parameter": ["i32", "i32", "i32"],          "immediate": [{"name": "dst_index",      "type": "varuint32"}, {"name": "src_index",  "type": "varuint32"}],"description": "copy table elements from dst_table[dstOffset, dstOffset + length] to src_table[srcOffset, srcOffset + length]", "extendedOp": 14 },
     81        "memory.fill":         { "category": "exttable",   "value":  252, "return": [],                              "parameter": ["i32", "i32", "i32"],          "immediate": [{"name": "unused",         "type": "uint8"}],                                                 "description": "sets all values in a region to a given byte", "extendedOp": 11 },
    8182        "call":                { "category": "call",       "value":  16, "return": ["call"],                         "parameter": ["call"],                       "immediate": [{"name": "function_index", "type": "varuint32"}],                                             "description": "call a function by its index" },
    8283        "call_indirect":       { "category": "call",       "value":  17, "return": ["call"],                         "parameter": ["call"],                       "immediate": [{"name": "type_index",     "type": "varuint32"}, {"name": "table_index","type": "varuint32"}],"description": "call a function indirect with an expected signature" },
  • trunk/Source/JavaScriptCore/ChangeLog

    r270827 r270855  
     12020-12-15  Dmitry Bezhetskov  <dbezhetskov@igalia.com>
     2
     3        [WASM-References] Add support for memory.fill
     4        https://bugs.webkit.org/show_bug.cgi?id=219848
     5
     6        Reviewed by Yusuke Suzuki.
     7
     8        Add support for memory.fill from ref-types spec.
     9        memory.fill sets all bytes in a memory region to a given byte:
     10        https://webassembly.github.io/reference-types/core/syntax/instructions.html#memory-instructions.
     11
     12        * bytecode/BytecodeList.rb:
     13        * llint/WebAssembly.asm:
     14        * wasm/WasmAirIRGenerator.cpp:
     15        (JSC::Wasm::AirIRGenerator::addMemoryFill):
     16        * wasm/WasmB3IRGenerator.cpp:
     17        (JSC::Wasm::B3IRGenerator::addMemoryFill):
     18        * wasm/WasmFunctionParser.h:
     19        (JSC::Wasm::FunctionParser<Context>::parseMemoryFillImmediate):
     20        (JSC::Wasm::FunctionParser<Context>::parseExpression):
     21        (JSC::Wasm::FunctionParser<Context>::parseUnreachableExpression):
     22        * wasm/WasmLLIntGenerator.cpp:
     23        (JSC::Wasm::LLIntGenerator::addMemoryFill):
     24        * wasm/WasmMemory.cpp:
     25        (JSC::Wasm::Memory::fill):
     26        (JSC::Wasm::Memory::doMemoryFill):
     27        * wasm/WasmMemory.h:
     28        * wasm/WasmOperations.cpp:
     29        (JSC::Wasm::JSC_DEFINE_JIT_OPERATION):
     30        * wasm/WasmOperations.h:
     31        * wasm/WasmSlowPaths.cpp:
     32        (JSC::LLInt::WASM_SLOW_PATH_DECL):
     33        * wasm/WasmSlowPaths.h:
     34        * wasm/wasm.json:
     35
    1362020-12-15  Dmitry Bezhetskov  <dbezhetskov@igalia.com>
    237
  • trunk/Source/JavaScriptCore/bytecode/BytecodeList.rb

    r270689 r270855  
    16761676    }
    16771677
     1678op :memory_fill,
     1679    args: {
     1680        dstAddress: VirtualRegister,
     1681        targetValue: VirtualRegister,
     1682        count: VirtualRegister,
     1683    }
     1684
    16781685op :select,
    16791686    args: {
  • trunk/Source/JavaScriptCore/interpreter/Register.h

    r270208 r270855  
    6464        ALWAYS_INLINE JSScope* scope() const;
    6565        int32_t unboxedInt32() const;
     66        uint32_t unboxedUInt32() const;
    6667        int32_t asanUnsafeUnboxedInt32() const;
    6768        int64_t unboxedInt52() const;
     
    143144    }
    144145
     146    ALWAYS_INLINE uint32_t Register::unboxedUInt32() const
     147    {
     148        return static_cast<uint32_t>(unboxedInt32());
     149    }
     150
    145151    SUPPRESS_ASAN ALWAYS_INLINE int32_t Register::asanUnsafeUnboxedInt32() const
    146152    {
  • trunk/Source/JavaScriptCore/llint/WebAssembly.asm

    r270689 r270855  
    568568slowWasmOp(table_copy)
    569569slowWasmOp(table_grow)
     570slowWasmOp(memory_fill)
    570571slowWasmOp(set_global_ref)
    571572slowWasmOp(set_global_ref_portable_binding)
  • trunk/Source/JavaScriptCore/wasm/WasmAirIRGenerator.cpp

    r270689 r270855  
    281281    PartialResult WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result);
    282282    PartialResult WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result);
     283    PartialResult WARN_UNUSED_RETURN addMemoryFill(ExpressionType dstAddress, ExpressionType targetValue, ExpressionType count);
    283284
    284285    // Atomics
     
    12711272    addShift(Type::I32, Urshift64, temp1, temp2, result);
    12721273    append(Move32, result, result);
     1274
     1275    return { };
     1276}
     1277
     1278auto AirIRGenerator::addMemoryFill(ExpressionType dstAddress, ExpressionType targetValue, ExpressionType count) -> PartialResult
     1279{
     1280    ASSERT(dstAddress.tmp());
     1281    ASSERT(dstAddress.type() == Type::I32);
     1282
     1283    ASSERT(targetValue.tmp());
     1284    ASSERT(targetValue.type() == Type::I32);
     1285
     1286    ASSERT(count.tmp());
     1287    ASSERT(count.type() == Type::I32);
     1288
     1289    auto result = tmpForType(Type::I32);
     1290    emitCCall(
     1291        &operationWasmMemoryFill, result, instanceValue(),
     1292        dstAddress, targetValue, count);
     1293
     1294    emitCheck([&] {
     1295        return Inst(BranchTest32, nullptr, Arg::resCond(MacroAssembler::Zero), result, result);
     1296    }, [=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
     1297        this->emitThrowException(jit, ExceptionType::OutOfBoundsTableAccess);
     1298    });
    12731299
    12741300    return { };
  • trunk/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp

    r270689 r270855  
    236236    PartialResult WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result);
    237237    PartialResult WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result);
     238    PartialResult WARN_UNUSED_RETURN addMemoryFill(ExpressionType dstAddress, ExpressionType targetValue, ExpressionType count);
    238239
    239240    // Atomics
     
    880881
    881882    result = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin(), numPages);
     883
     884    return { };
     885}
     886
     887auto B3IRGenerator::addMemoryFill(ExpressionType dstAddress, ExpressionType targetValue, ExpressionType count) -> PartialResult
     888{
     889    auto result = m_currentBlock->appendNew<CCallValue>(
     890        m_proc, toB3Type(I32), origin(),
     891        m_currentBlock->appendNew<ConstPtrValue>(m_proc, origin(), tagCFunction<OperationPtrTag>(operationWasmMemoryFill)),
     892        instanceValue(),
     893        dstAddress, targetValue, count);
     894
     895    {
     896        CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, origin(),
     897            m_currentBlock->appendNew<Value>(m_proc, Equal, origin(), result, m_currentBlock->appendNew<Const32Value>(m_proc, origin(), 0)));
     898
     899        check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) {
     900            this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTableAccess);
     901        });
     902    }
    882903
    883904    return { };
  • trunk/Source/JavaScriptCore/wasm/WasmFunctionParser.h

    r270827 r270855  
    157157    PartialResult WARN_UNUSED_RETURN parseAnnotatedSelectImmediates(AnnotatedSelectImmediates&);
    158158
     159    PartialResult WARN_UNUSED_RETURN parseMemoryFillImmediate();
     160
    159161#define WASM_TRY_ADD_TO_CONTEXT(add_expression) WASM_FAIL_IF_HELPER_FAILS(m_context.add_expression)
    160162
     
    565567    result.sizeOfAnnotationVector = sizeOfAnnotationVector;
    566568    result.targetType = targetType;
    567 
     569    return { };
     570}
     571
     572template<typename Context>
     573auto FunctionParser<Context>::parseMemoryFillImmediate() -> PartialResult
     574{
     575    uint8_t auxiliaryByte;
     576    WASM_PARSER_FAIL_IF(!parseUInt8(auxiliaryByte), "can't parse auxiliary byte");
     577    WASM_PARSER_FAIL_IF(!!auxiliaryByte, "auxiliary byte for memory.fill should be zero, but got ", auxiliaryByte);
    568578    return { };
    569579}
     
    816826
    817827            WASM_TRY_ADD_TO_CONTEXT(addTableCopy(immediates.dstTableIndex, immediates.srcTableIndex, dstOffset, srcOffset, length));
     828            break;
     829        }
     830        case ExtTableOpType::MemoryFill: {
     831            WASM_FAIL_IF_HELPER_FAILS(parseMemoryFillImmediate());
     832
     833            WASM_VALIDATOR_FAIL_IF(!m_info.memoryCount(), "memory must be present");
     834
     835            TypedExpression dstAddress;
     836            TypedExpression targetValue;
     837            TypedExpression count;
     838
     839            WASM_TRY_POP_EXPRESSION_STACK_INTO(count, "memory.fill");
     840            WASM_TRY_POP_EXPRESSION_STACK_INTO(targetValue, "memory.fill");
     841            WASM_TRY_POP_EXPRESSION_STACK_INTO(dstAddress, "memory.fill");
     842
     843            WASM_VALIDATOR_FAIL_IF(I32 != dstAddress.type(), "memory.fill dstAddress to type ", dstAddress.type(), " expected ", I32);
     844            WASM_VALIDATOR_FAIL_IF(I32 != targetValue.type(), "memory.fill targetValue to type ", targetValue.type(), " expected ", I32);
     845            WASM_VALIDATOR_FAIL_IF(I32 != count.type(), "memory.fill size to type ", count.type(), " expected ", I32);
     846
     847            WASM_TRY_ADD_TO_CONTEXT(addMemoryFill(dstAddress, targetValue, count));
    818848            break;
    819849        }
     
    13861416            return { };
    13871417        }
     1418        case ExtTableOpType::MemoryFill: {
     1419            WASM_FAIL_IF_HELPER_FAILS(parseMemoryFillImmediate());
     1420            return { };
     1421        }
    13881422        default:
    13891423            WASM_PARSER_FAIL_IF(true, "invalid extended table op ", extOp);
  • trunk/Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp

    r270689 r270855  
    212212    PartialResult WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result);
    213213    PartialResult WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result);
     214    PartialResult WARN_UNUSED_RETURN addMemoryFill(ExpressionType dstAddress, ExpressionType targetValue, ExpressionType count);
    214215
    215216    // Atomics
     
    11881189}
    11891190
     1191auto LLIntGenerator::addMemoryFill(ExpressionType dstAddress, ExpressionType targetValue, ExpressionType count) -> PartialResult
     1192{
     1193    WasmMemoryFill::emit(this, dstAddress, targetValue, count);
     1194    return { };
     1195}
     1196
    11901197auto LLIntGenerator::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> PartialResult
    11911198{
  • trunk/Source/JavaScriptCore/wasm/WasmMemory.cpp

    r269974 r270855  
    3131
    3232#include "Options.h"
     33#include <wtf/CheckedArithmetic.h>
    3334#include <wtf/DataLog.h>
    3435#include <wtf/Gigacage.h>
     
    618619}
    619620
     621bool Memory::fill(uint32_t offset, uint8_t targetValue, uint32_t count)
     622{
     623    if (sumOverflows<uint32_t>(offset, count))
     624        return false;
     625
     626    if (offset + count > m_handle->size())
     627        return false;
     628
     629    memset(reinterpret_cast<uint8_t*>(memory()) + offset, targetValue, count);
     630    return true;
     631}
     632
    620633void Memory::registerInstance(Instance* instance)
    621634{
  • trunk/Source/JavaScriptCore/wasm/WasmMemory.h

    r270208 r270855  
    133133    };
    134134    Expected<PageCount, GrowFailReason> grow(PageCount);
     135    bool fill(uint32_t, uint8_t, uint32_t);
    135136    void registerInstance(Instance*);
    136137
  • trunk/Source/JavaScriptCore/wasm/WasmOperations.cpp

    r270689 r270855  
    639639}
    640640
     641JSC_DEFINE_JIT_OPERATION(operationWasmMemoryFill, bool, (Instance* instance, uint32_t dstAddress, uint32_t targetValue, uint32_t count))
     642{
     643    return instance->memory()->fill(dstAddress, static_cast<uint8_t>(targetValue), count);
     644}
     645
    641646JSC_DEFINE_JIT_OPERATION(operationGetWasmTableElement, EncodedJSValue, (Instance* instance, unsigned tableIndex, int32_t signedIndex))
    642647{
  • trunk/Source/JavaScriptCore/wasm/WasmOperations.h

    r270689 r270855  
    6262JSC_DECLARE_JIT_OPERATION(operationPopcount64, uint64_t, (int64_t));
    6363JSC_DECLARE_JIT_OPERATION(operationGrowMemory, int32_t, (void*, Instance*, int32_t));
     64JSC_DECLARE_JIT_OPERATION(operationWasmMemoryFill, bool, (Instance*, uint32_t dstAddress, uint32_t targetValue, uint32_t count));
    6465
    6566JSC_DECLARE_JIT_OPERATION(operationGetWasmTableElement, EncodedJSValue, (Instance*, unsigned, int32_t));
  • trunk/Source/JavaScriptCore/wasm/WasmSlowPaths.cpp

    r270689 r270855  
    380380}
    381381
     382WASM_SLOW_PATH_DECL(memory_fill)
     383{
     384    auto instruction = pc->as<WasmMemoryFill, WasmOpcodeTraits>();
     385    uint32_t dstAddress = READ(instruction.m_dstAddress).unboxedUInt32();
     386    uint32_t targetValue = READ(instruction.m_targetValue).unboxedUInt32();
     387    uint32_t count = READ(instruction.m_count).unboxedUInt32();
     388    if (!Wasm::operationWasmMemoryFill(instance, dstAddress, targetValue, count))
     389        WASM_THROW(Wasm::ExceptionType::OutOfBoundsTableAccess);
     390    WASM_END();
     391}
     392
    382393inline SlowPathReturnType doWasmCall(Wasm::Instance* instance, unsigned functionIndex)
    383394{
  • trunk/Source/JavaScriptCore/wasm/WasmSlowPaths.h

    r270689 r270855  
    6767WASM_SLOW_PATH_HIDDEN_DECL(table_grow);
    6868WASM_SLOW_PATH_HIDDEN_DECL(grow_memory);
     69WASM_SLOW_PATH_HIDDEN_DECL(memory_fill);
    6970WASM_SLOW_PATH_HIDDEN_DECL(call);
    7071WASM_SLOW_PATH_HIDDEN_DECL(call_no_tls);
  • trunk/Source/JavaScriptCore/wasm/wasm.json

    r270827 r270855  
    7979        "table.fill":          { "category": "exttable",   "value":  252, "return": [],                              "parameter": ["i32", "externref", "i32"],    "immediate": [{"name": "table_index",    "type": "varuint32"}],                                             "description": "fill entries [i,i+n) with the given value", "extendedOp": 17 },
    8080        "table.copy":          { "category": "exttable",   "value":  252, "return": [],                              "parameter": ["i32", "i32", "i32"],          "immediate": [{"name": "dst_index",      "type": "varuint32"}, {"name": "src_index",  "type": "varuint32"}],"description": "copy table elements from dst_table[dstOffset, dstOffset + length] to src_table[srcOffset, srcOffset + length]", "extendedOp": 14 },
     81        "memory.fill":         { "category": "exttable",   "value":  252, "return": [],                              "parameter": ["i32", "i32", "i32"],          "immediate": [{"name": "unused",         "type": "uint8"}],                                                 "description": "sets all values in a region to a given byte", "extendedOp": 11 },
    8182        "call":                { "category": "call",       "value":  16, "return": ["call"],                         "parameter": ["call"],                       "immediate": [{"name": "function_index", "type": "varuint32"}],                                             "description": "call a function by its index" },
    8283        "call_indirect":       { "category": "call",       "value":  17, "return": ["call"],                         "parameter": ["call"],                       "immediate": [{"name": "type_index",     "type": "varuint32"}, {"name": "table_index","type": "varuint32"}],"description": "call a function indirect with an expected signature" },
Note: See TracChangeset for help on using the changeset viewer.