diff options
author | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@qt.io> | 2017-05-30 12:48:17 +0200 |
commit | 881da28418d380042aa95a97f0cbd42560a64f7c (patch) | |
tree | a794dff3274695e99c651902dde93d934ea7a5af /Source/JavaScriptCore/wasm | |
parent | 7e104c57a70fdf551bb3d22a5d637cdcbc69dbea (diff) | |
parent | 0fcedcd17cc00d3dd44c718b3cb36c1033319671 (diff) | |
download | qtwebkit-881da28418d380042aa95a97f0cbd42560a64f7c.tar.gz |
Merge 'wip/next' into dev
Change-Id: Iff9ee5e23bb326c4371ec8ed81d56f2f05d680e9
Diffstat (limited to 'Source/JavaScriptCore/wasm')
-rw-r--r-- | Source/JavaScriptCore/wasm/JSWASMModule.cpp | 68 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/JSWASMModule.h | 118 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMConstants.h | 329 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMFormat.h | 98 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMFunctionB3IRGenerator.h | 394 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMFunctionCompiler.h | 1541 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMFunctionParser.cpp | 1225 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMFunctionParser.h | 148 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMFunctionSyntaxChecker.h | 287 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMModuleParser.cpp | 377 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMModuleParser.h | 77 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMReader.cpp | 249 | ||||
-rw-r--r-- | Source/JavaScriptCore/wasm/WASMReader.h | 79 |
13 files changed, 4990 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/wasm/JSWASMModule.cpp b/Source/JavaScriptCore/wasm/JSWASMModule.cpp new file mode 100644 index 000000000..c7ea4a6ed --- /dev/null +++ b/Source/JavaScriptCore/wasm/JSWASMModule.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSWASMModule.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSArrayBuffer.h" +#include "JSCJSValueInlines.h" +#include "JSCellInlines.h" +#include "JSFunction.h" +#include "SlotVisitorInlines.h" + +namespace JSC { + +const ClassInfo JSWASMModule::s_info = { "WASMModule", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWASMModule) }; + +JSWASMModule::JSWASMModule(VM& vm, Structure* structure, JSArrayBuffer* arrayBuffer) + : Base(vm, structure) +{ + if (arrayBuffer) + m_arrayBuffer.set(vm, this, arrayBuffer); +} + +void JSWASMModule::destroy(JSCell* cell) +{ + JSWASMModule* thisObject = jsCast<JSWASMModule*>(cell); + thisObject->JSWASMModule::~JSWASMModule(); +} + +void JSWASMModule::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSWASMModule* thisObject = jsCast<JSWASMModule*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_arrayBuffer); + for (auto function : thisObject->m_functions) + visitor.append(&function); + for (auto importedFunction : thisObject->m_importedFunctions) + visitor.append(&importedFunction); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/JSWASMModule.h b/Source/JavaScriptCore/wasm/JSWASMModule.h new file mode 100644 index 000000000..2d16791cc --- /dev/null +++ b/Source/JavaScriptCore/wasm/JSWASMModule.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSWASMModule_h +#define JSWASMModule_h + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "WASMFormat.h" + +namespace JSC { + +class JSWASMModule : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + union GlobalVariable { + GlobalVariable(int32_t value) + : intValue(value) + { + } + GlobalVariable(float value) + : floatValue(value) + { + } + GlobalVariable(double value) + : doubleValue(value) + { + } + + int32_t intValue; + float floatValue; + double doubleValue; + }; + + static JSWASMModule* create(VM& vm, Structure* structure, JSArrayBuffer* arrayBuffer) + { + JSWASMModule* module = new (NotNull, allocateCell<JSWASMModule>(vm.heap)) JSWASMModule(vm, structure, arrayBuffer); + module->finishCreation(vm); + return module; + } + + DECLARE_INFO; + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject) + { + return Structure::create(vm, globalObject, jsNull(), TypeInfo(ObjectType, StructureFlags), info()); + } + + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + + Vector<uint32_t>& i32Constants() { return m_i32Constants; } + Vector<float>& f32Constants() { return m_f32Constants; } + Vector<double>& f64Constants() { return m_f64Constants; } + Vector<WASMSignature>& signatures() { return m_signatures; } + Vector<WASMFunctionImport>& functionImports() { return m_functionImports; } + Vector<WASMFunctionImportSignature>& functionImportSignatures() { return m_functionImportSignatures; } + Vector<WASMType>& globalVariableTypes() { return m_globalVariableTypes; } + Vector<WASMFunctionDeclaration>& functionDeclarations() { return m_functionDeclarations; } + Vector<WASMFunctionPointerTable>& functionPointerTables() { return m_functionPointerTables; } + + const JSArrayBuffer* arrayBuffer() const { return m_arrayBuffer.get(); } + Vector<WriteBarrier<JSFunction>>& functions() { return m_functions; } + Vector<unsigned>& functionStartOffsetsInSource() { return m_functionStartOffsetsInSource; } + Vector<unsigned>& functionStackHeights() { return m_functionStackHeights; } + Vector<GlobalVariable>& globalVariables() { return m_globalVariables; } + Vector<WriteBarrier<JSFunction>>& importedFunctions() { return m_importedFunctions; } + +private: + JSWASMModule(VM&, Structure*, JSArrayBuffer*); + + Vector<uint32_t> m_i32Constants; + Vector<float> m_f32Constants; + Vector<double> m_f64Constants; + Vector<WASMSignature> m_signatures; + Vector<WASMFunctionImport> m_functionImports; + Vector<WASMFunctionImportSignature> m_functionImportSignatures; + Vector<WASMType> m_globalVariableTypes; + Vector<WASMFunctionDeclaration> m_functionDeclarations; + Vector<WASMFunctionPointerTable> m_functionPointerTables; + + WriteBarrier<JSArrayBuffer> m_arrayBuffer; + Vector<WriteBarrier<JSFunction>> m_functions; + Vector<unsigned> m_functionStartOffsetsInSource; + Vector<unsigned> m_functionStackHeights; + Vector<GlobalVariable> m_globalVariables; + Vector<WriteBarrier<JSFunction>> m_importedFunctions; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // JSWASMModule_h diff --git a/Source/JavaScriptCore/wasm/WASMConstants.h b/Source/JavaScriptCore/wasm/WASMConstants.h new file mode 100644 index 000000000..4e3bd443f --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMConstants.h @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ========================================================================= + * + * Copyright (c) 2015 by the repository authors of + * WebAssembly/polyfill-prototype-1. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WASMConstants_h +#define WASMConstants_h + +#if ENABLE(WEBASSEMBLY) + +namespace JSC { + +static const uint32_t wasmMagicNumber = 0x6d736177; + +enum class WASMOpKind { + Statement, + Expression +}; + +enum class WASMOpStatement : uint8_t { + SetLocal, + SetGlobal, + I32Store8, + I32StoreWithOffset8, + I32Store16, + I32StoreWithOffset16, + I32Store32, + I32StoreWithOffset32, + F32Store, + F32StoreWithOffset, + F64Store, + F64StoreWithOffset, + CallInternal, + CallIndirect, + CallImport, + Return, + Block, + If, + IfElse, + While, + Do, + Label, + Break, + BreakLabel, + Continue, + ContinueLabel, + Switch, + NumberOfWASMOpStatements +}; + +enum class WASMOpStatementWithImmediate : uint8_t { + SetLocal, + SetGlobal, + NumberOfWASMOpStatementWithImmediates +}; + +enum class WASMOpExpressionI32 : uint8_t { + ConstantPoolIndex, + Immediate, + GetLocal, + GetGlobal, + SetLocal, + SetGlobal, + SLoad8, + SLoadWithOffset8, + ULoad8, + ULoadWithOffset8, + SLoad16, + SLoadWithOffset16, + ULoad16, + ULoadWithOffset16, + Load32, + LoadWithOffset32, + Store8, + StoreWithOffset8, + Store16, + StoreWithOffset16, + Store32, + StoreWithOffset32, + CallInternal, + CallIndirect, + CallImport, + Conditional, + Comma, + FromF32, + FromF64, + Negate, + Add, + Sub, + Mul, + SDiv, + UDiv, + SMod, + UMod, + BitNot, + BitOr, + BitAnd, + BitXor, + LeftShift, + ArithmeticRightShift, + LogicalRightShift, + CountLeadingZeros, + LogicalNot, + EqualI32, + EqualF32, + EqualF64, + NotEqualI32, + NotEqualF32, + NotEqualF64, + SLessThanI32, + ULessThanI32, + LessThanF32, + LessThanF64, + SLessThanOrEqualI32, + ULessThanOrEqualI32, + LessThanOrEqualF32, + LessThanOrEqualF64, + SGreaterThanI32, + UGreaterThanI32, + GreaterThanF32, + GreaterThanF64, + SGreaterThanOrEqualI32, + UGreaterThanOrEqualI32, + GreaterThanOrEqualF32, + GreaterThanOrEqualF64, + SMin, + UMin, + SMax, + UMax, + Abs, + NumberOfWASMOpExpressionI32s +}; + +enum class WASMOpExpressionI32WithImmediate : uint8_t { + ConstantPoolIndex, + Immediate, + GetLocal, + NumberOfWASMOpExpressionI32WithImmediates +}; + +enum class WASMOpExpressionF32 : uint8_t { + ConstantPoolIndex, + Immediate, + GetLocal, + GetGlobal, + SetLocal, + SetGlobal, + Load, + LoadWithOffset, + Store, + StoreWithOffset, + CallInternal, + CallIndirect, + Conditional, + Comma, + FromS32, + FromU32, + FromF64, + Negate, + Add, + Sub, + Mul, + Div, + Abs, + Ceil, + Floor, + Sqrt, + NumberOfWASMOpExpressionF32s +}; + +enum class WASMOpExpressionF32WithImmediate : uint8_t { + ConstantPoolIndex, + GetLocal, + NumberOfWASMOpExpressionF32WithImmediates +}; + +enum class WASMOpExpressionF64 : uint8_t { + ConstantPoolIndex, + Immediate, + GetLocal, + GetGlobal, + SetLocal, + SetGlobal, + Load, + LoadWithOffset, + Store, + StoreWithOffset, + CallInternal, + CallIndirect, + CallImport, + Conditional, + Comma, + FromS32, + FromU32, + FromF32, + Negate, + Add, + Sub, + Mul, + Div, + Mod, + Min, + Max, + Abs, + Ceil, + Floor, + Sqrt, + Cos, + Sin, + Tan, + ACos, + ASin, + ATan, + ATan2, + Exp, + Ln, + Pow, + NumberOfWASMOpExpressionF64s +}; + +enum class WASMOpExpressionF64WithImmediate : uint8_t { + ConstantPoolIndex, + GetLocal, + NumberOfWASMOpExpressionF64WithImmediates +}; + +enum class WASMOpExpressionVoid : uint8_t { + CallInternal, + CallIndirect, + CallImport, + NumberOfWASMOpExpressionVoids +}; + +enum class WASMVariableTypes : uint8_t { + I32 = 1 << 0, + F32 = 1 << 1, + F64 = 1 << 2, + NumberOfVariableTypes = 8 +}; + +enum class WASMVariableTypesWithImmediate : uint8_t { + I32, + NumberOfVariableTypesWithImmediates +}; + +enum class WASMSwitchCase : uint8_t { + CaseWithNoStatements, + CaseWithStatement, + CaseWithBlockStatement, + DefaultWithNoStatements, + DefaultWithStatement, + DefaultWithBlockStatement, + NumberOfSwitchCases +}; + +enum class WASMExportFormat : uint8_t { + Default, + Record, + NumberOfExportFormats +}; + +enum class WASMTypeConversion { + ConvertSigned, + ConvertUnsigned, + Promote, + Demote, +}; + +enum class WASMMemoryType { + I8, + I16, + I32, + F32, + F64 +}; + +enum class MemoryAccessOffsetMode { NoOffset, WithOffset }; +enum class MemoryAccessConversion { NoConversion, SignExtend, ZeroExtend }; + +static const uint8_t hasImmediateInOpFlag = 0x80; + +static const unsigned opWithImmediateBits = 2; +static const uint32_t opWithImmediateLimit = 1 << opWithImmediateBits; + +static const unsigned immediateBits = 5; +static const uint32_t immediateLimit = 1 << immediateBits; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // WASMConstants_h diff --git a/Source/JavaScriptCore/wasm/WASMFormat.h b/Source/JavaScriptCore/wasm/WASMFormat.h new file mode 100644 index 000000000..b36341056 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMFormat.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ========================================================================= + * + * Copyright (c) 2015 by the repository authors of + * WebAssembly/polyfill-prototype-1. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WASMFormat_h +#define WASMFormat_h + +#if ENABLE(WEBASSEMBLY) + +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace JSC { + +class JSFunction; + +enum class WASMType : uint8_t { + I32, + F32, + F64, + NumberOfTypes +}; + +enum class WASMExpressionType : uint8_t { + I32, + F32, + F64, + Void, + NumberOfExpressionTypes +}; + +struct WASMSignature { + WASMExpressionType returnType; + Vector<WASMType> arguments; +}; + +struct WASMFunctionImport { + String functionName; +}; + +struct WASMFunctionImportSignature { + uint32_t signatureIndex; + uint32_t functionImportIndex; +}; + +struct WASMFunctionDeclaration { + uint32_t signatureIndex; +}; + +struct WASMFunctionPointerTable { + uint32_t signatureIndex; + Vector<uint32_t> functionIndices; + Vector<JSFunction*> functions; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // WASMFormat_h diff --git a/Source/JavaScriptCore/wasm/WASMFunctionB3IRGenerator.h b/Source/JavaScriptCore/wasm/WASMFunctionB3IRGenerator.h new file mode 100644 index 000000000..7813a23da --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMFunctionB3IRGenerator.h @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WASMFunctionB3IRGenerator_h +#define WASMFunctionB3IRGenerator_h + +#if ENABLE(WEBASSEMBLY) && ENABLE(FTL_JIT) + +#include "FTLAbbreviatedTypes.h" + +#define UNUSED 0 + +namespace JSC { + +using FTL::LBasicBlock; +using FTL::LValue; + +class WASMFunctionB3IRGenerator { +public: + typedef LValue Expression; + typedef int Statement; + typedef Vector<LValue> ExpressionList; + struct MemoryAddress { + MemoryAddress(void*) { } + MemoryAddress(LValue index, uint32_t offset) + : index(index) + , offset(offset) + { + } + LValue index; + uint32_t offset; + }; + typedef LBasicBlock JumpTarget; + enum class JumpCondition { Zero, NonZero }; + + void startFunction(const Vector<WASMType>& arguments, uint32_t numberOfI32LocalVariables, uint32_t numberOfF32LocalVariables, uint32_t numberOfF64LocalVariables) + { + // FIXME: Implement this method. + UNUSED_PARAM(arguments); + UNUSED_PARAM(numberOfI32LocalVariables); + UNUSED_PARAM(numberOfF32LocalVariables); + UNUSED_PARAM(numberOfF64LocalVariables); + } + + void endFunction() + { + // FIXME: Implement this method. + } + + LValue buildSetLocal(WASMOpKind opKind, uint32_t localIndex, LValue value, WASMType type) + { + // FIXME: Implement this method. + UNUSED_PARAM(opKind); + UNUSED_PARAM(localIndex); + UNUSED_PARAM(value); + UNUSED_PARAM(type); + return UNUSED; + } + + LValue buildSetGlobal(WASMOpKind opKind, uint32_t globalIndex, LValue value, WASMType type) + { + // FIXME: Implement this method. + UNUSED_PARAM(opKind); + UNUSED_PARAM(globalIndex); + UNUSED_PARAM(value); + UNUSED_PARAM(type); + return UNUSED; + } + + void buildReturn(LValue value, WASMExpressionType returnType) + { + // FIXME: Implement this method. + UNUSED_PARAM(value); + UNUSED_PARAM(returnType); + } + + LValue buildImmediateI32(uint32_t immediate) + { + // FIXME: Implement this method. + UNUSED_PARAM(immediate); + return UNUSED; + } + + LValue buildImmediateF32(float immediate) + { + // FIXME: Implement this method. + UNUSED_PARAM(immediate); + return UNUSED; + } + + LValue buildImmediateF64(double immediate) + { + // FIXME: Implement this method. + UNUSED_PARAM(immediate); + return UNUSED; + } + + LValue buildGetLocal(uint32_t localIndex, WASMType type) + { + // FIXME: Implement this method. + UNUSED_PARAM(localIndex); + UNUSED_PARAM(type); + return UNUSED; + } + + LValue buildGetGlobal(uint32_t globalIndex, WASMType type) + { + // FIXME: Implement this method. + UNUSED_PARAM(globalIndex); + UNUSED_PARAM(type); + return UNUSED; + } + + LValue buildConvertType(LValue value, WASMExpressionType fromType, WASMExpressionType toType, WASMTypeConversion conversion) + { + // FIXME: Implement this method. + UNUSED_PARAM(value); + UNUSED_PARAM(fromType); + UNUSED_PARAM(toType); + UNUSED_PARAM(conversion); + return UNUSED; + } + + LValue buildLoad(const MemoryAddress& memoryAddress, WASMExpressionType expressionType, WASMMemoryType memoryType, MemoryAccessConversion conversion) + { + // FIXME: Implement this method. + UNUSED_PARAM(memoryAddress); + UNUSED_PARAM(expressionType); + UNUSED_PARAM(memoryType); + UNUSED_PARAM(conversion); + return UNUSED; + } + + LValue buildStore(WASMOpKind opKind, const MemoryAddress& memoryAddress, WASMExpressionType expressionType, WASMMemoryType memoryType, LValue value) + { + // FIXME: Implement this method. + UNUSED_PARAM(opKind); + UNUSED_PARAM(memoryAddress); + UNUSED_PARAM(expressionType); + UNUSED_PARAM(memoryType); + UNUSED_PARAM(value); + return UNUSED; + } + + LValue buildUnaryI32(LValue value, WASMOpExpressionI32 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(value); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildUnaryF32(LValue value, WASMOpExpressionF32 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(value); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildUnaryF64(LValue value, WASMOpExpressionF64 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(value); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildBinaryI32(LValue left, LValue right, WASMOpExpressionI32 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(left); + UNUSED_PARAM(right); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildBinaryF32(LValue left, LValue right, WASMOpExpressionF32 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(left); + UNUSED_PARAM(right); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildBinaryF64(LValue left, LValue right, WASMOpExpressionF64 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(left); + UNUSED_PARAM(right); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildRelationalI32(LValue left, LValue right, WASMOpExpressionI32 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(left); + UNUSED_PARAM(right); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildRelationalF32(LValue left, LValue right, WASMOpExpressionI32 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(left); + UNUSED_PARAM(right); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildRelationalF64(LValue left, LValue right, WASMOpExpressionI32 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(left); + UNUSED_PARAM(right); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildMinOrMaxI32(LValue left, LValue right, WASMOpExpressionI32 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(left); + UNUSED_PARAM(right); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildMinOrMaxF64(LValue left, LValue right, WASMOpExpressionF64 op) + { + // FIXME: Implement this method. + UNUSED_PARAM(left); + UNUSED_PARAM(right); + UNUSED_PARAM(op); + return UNUSED; + } + + LValue buildCallInternal(uint32_t functionIndex, const Vector<LValue>& argumentList, const WASMSignature& signature, WASMExpressionType returnType) + { + // FIXME: Implement this method. + UNUSED_PARAM(functionIndex); + UNUSED_PARAM(argumentList); + UNUSED_PARAM(signature); + UNUSED_PARAM(returnType); + return UNUSED; + } + + LValue buildCallIndirect(uint32_t functionPointerTableIndex, LValue index, const Vector<LValue>& argumentList, const WASMSignature& signature, WASMExpressionType returnType) + { + // FIXME: Implement this method. + UNUSED_PARAM(functionPointerTableIndex); + UNUSED_PARAM(index); + UNUSED_PARAM(argumentList); + UNUSED_PARAM(signature); + UNUSED_PARAM(returnType); + return UNUSED; + } + + LValue buildCallImport(uint32_t functionImportIndex, const Vector<LValue>& argumentList, const WASMSignature& signature, WASMExpressionType returnType) + { + // FIXME: Implement this method. + UNUSED_PARAM(functionImportIndex); + UNUSED_PARAM(argumentList); + UNUSED_PARAM(signature); + UNUSED_PARAM(returnType); + return UNUSED; + } + + void appendExpressionList(Vector<LValue>& expressionList, LValue value) + { + // FIXME: Implement this method. + UNUSED_PARAM(expressionList); + UNUSED_PARAM(value); + } + + void discard(LValue) + { + } + + void linkTarget(LBasicBlock target) + { + // FIXME: Implement this method. + UNUSED_PARAM(target); + } + + void jumpToTarget(LBasicBlock target) + { + // FIXME: Implement this method. + UNUSED_PARAM(target); + } + + void jumpToTargetIf(JumpCondition, LValue value, LBasicBlock target) + { + // FIXME: Implement this method. + UNUSED_PARAM(value); + UNUSED_PARAM(target); + } + + void startLoop() + { + // FIXME: Implement this method. + } + + void endLoop() + { + // FIXME: Implement this method. + } + + void startSwitch() + { + // FIXME: Implement this method. + } + + void endSwitch() + { + // FIXME: Implement this method. + } + + void startLabel() + { + // FIXME: Implement this method. + } + + void endLabel() + { + // FIXME: Implement this method. + } + + LBasicBlock breakTarget() + { + // FIXME: Implement this method. + return UNUSED; + } + + LBasicBlock continueTarget() + { + // FIXME: Implement this method. + return UNUSED; + } + + LBasicBlock breakLabelTarget(uint32_t labelIndex) + { + // FIXME: Implement this method. + UNUSED_PARAM(labelIndex); + return UNUSED; + } + + LBasicBlock continueLabelTarget(uint32_t labelIndex) + { + // FIXME: Implement this method. + UNUSED_PARAM(labelIndex); + return UNUSED; + } + + void buildSwitch(LValue value, const Vector<int64_t>& cases, const Vector<LBasicBlock>& targets, LBasicBlock defaultTarget) + { + // FIXME: Implement this method. + UNUSED_PARAM(value); + UNUSED_PARAM(cases); + UNUSED_PARAM(targets); + UNUSED_PARAM(defaultTarget); + } +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // WASMFunctionB3IRGenerator_h diff --git a/Source/JavaScriptCore/wasm/WASMFunctionCompiler.h b/Source/JavaScriptCore/wasm/WASMFunctionCompiler.h new file mode 100644 index 000000000..de439ba07 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMFunctionCompiler.h @@ -0,0 +1,1541 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WASMFunctionCompiler_h +#define WASMFunctionCompiler_h + +#if ENABLE(WEBASSEMBLY) + +#include "BinarySwitch.h" +#include "CCallHelpers.h" +#include "JIT.h" +#include "JITOperations.h" +#include "JSArrayBuffer.h" +#include "LinkBuffer.h" +#include "MaxFrameExtentForSlowPathCall.h" + +#define UNUSED 0 + +namespace JSC { + +static int32_t JIT_OPERATION operationConvertJSValueToInt32(ExecState* exec, EncodedJSValue value) +{ + return JSValue::decode(value).toInt32(exec); +} + +static double JIT_OPERATION operationConvertJSValueToDouble(ExecState* exec, EncodedJSValue value) +{ + return JSValue::decode(value).toNumber(exec); +} + +#if !CPU(X86) && !CPU(X86_64) +static int32_t JIT_OPERATION operationDiv(int32_t left, int32_t right) +{ + return left / right; +} + +static int32_t JIT_OPERATION operationMod(int32_t left, int32_t right) +{ + return left % right; +} + +static uint32_t JIT_OPERATION operationUnsignedDiv(uint32_t left, uint32_t right) +{ + return left / right; +} + +static uint32_t JIT_OPERATION operationUnsignedMod(uint32_t left, uint32_t right) +{ + return left % right; +} +#endif + +#if !USE(JSVALUE64) +static double JIT_OPERATION operationConvertUnsignedInt32ToDouble(uint32_t value) +{ + return static_cast<double>(value); +} +#endif + +static size_t sizeOfMemoryType(WASMMemoryType memoryType) +{ + switch (memoryType) { + case WASMMemoryType::I8: + return 1; + case WASMMemoryType::I16: + return 2; + case WASMMemoryType::I32: + case WASMMemoryType::F32: + return 4; + case WASMMemoryType::F64: + return 8; + default: + ASSERT_NOT_REACHED(); + } + RELEASE_ASSERT_NOT_REACHED(); + return 0; +} + +class WASMFunctionCompiler : private CCallHelpers { +public: + typedef int Expression; + typedef int Statement; + typedef int ExpressionList; + struct MemoryAddress { + MemoryAddress(void*) { } + MemoryAddress(int, uint32_t offset) + : offset(offset) + { + } + uint32_t offset; + }; + struct JumpTarget { + Label label; + JumpList jumpList; + }; + enum class JumpCondition { Zero, NonZero }; + + WASMFunctionCompiler(VM& vm, CodeBlock* codeBlock, JSWASMModule* module, unsigned stackHeight) + : CCallHelpers(&vm, codeBlock) + , m_module(module) + , m_stackHeight(stackHeight) + { + } + + void startFunction(const Vector<WASMType>& arguments, uint32_t numberOfI32LocalVariables, uint32_t numberOfF32LocalVariables, uint32_t numberOfF64LocalVariables) + { + m_calleeSaveSpace = WTF::roundUpToMultipleOf(sizeof(StackSlot), RegisterSet::webAssemblyCalleeSaveRegisters().numberOfSetRegisters() * sizeof(void*)); + m_codeBlock->setCalleeSaveRegisters(RegisterSet::webAssemblyCalleeSaveRegisters()); + + emitFunctionPrologue(); + emitPutToCallFrameHeader(m_codeBlock, JSStack::CodeBlock); + + m_beginLabel = label(); + + addPtr(TrustedImm32(-m_calleeSaveSpace - WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_stackHeight) * sizeof(StackSlot) - maxFrameExtentForSlowPathCall), GPRInfo::callFrameRegister, GPRInfo::regT1); + m_stackOverflow = branchPtr(Above, AbsoluteAddress(m_vm->addressOfStackLimit()), GPRInfo::regT1); + + move(GPRInfo::regT1, stackPointerRegister); + checkStackPointerAlignment(); + + emitSaveCalleeSaves(); + emitMaterializeTagCheckRegisters(); + + m_numberOfLocals = arguments.size() + numberOfI32LocalVariables + numberOfF32LocalVariables + numberOfF64LocalVariables; + + unsigned localIndex = 0; + for (size_t i = 0; i < arguments.size(); ++i) { + // FIXME: No need to do type conversion if the caller is a WebAssembly function. + // https://bugs.webkit.org/show_bug.cgi?id=149310 + Address address(GPRInfo::callFrameRegister, CallFrame::argumentOffset(i) * sizeof(Register)); +#if USE(JSVALUE64) + JSValueRegs valueRegs(GPRInfo::regT0); +#else + JSValueRegs valueRegs(GPRInfo::regT1, GPRInfo::regT0); +#endif + loadValue(address, valueRegs); + switch (arguments[i]) { + case WASMType::I32: + convertValueToInt32(valueRegs, GPRInfo::regT0); + store32(GPRInfo::regT0, localAddress(localIndex++)); + break; + case WASMType::F32: + case WASMType::F64: +#if USE(JSVALUE64) + convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::regT1); +#else + convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::regT2, FPRInfo::fpRegT1); +#endif + if (arguments[i] == WASMType::F32) + convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, localAddress(localIndex++)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + for (uint32_t i = 0; i < numberOfI32LocalVariables; ++i) + store32(TrustedImm32(0), localAddress(localIndex++)); + for (uint32_t i = 0; i < numberOfF32LocalVariables; ++i) + store32(TrustedImm32(0), localAddress(localIndex++)); + for (uint32_t i = 0; i < numberOfF64LocalVariables; ++i) { +#if USE(JSVALUE64) + store64(TrustedImm64(0), localAddress(localIndex++)); +#else + store32(TrustedImm32(0), localAddress(localIndex)); + store32(TrustedImm32(0), localAddress(localIndex).withOffset(4)); + localIndex++; +#endif + } + + m_codeBlock->setNumParameters(1 + arguments.size()); + } + + void endFunction() + { + ASSERT(!m_tempStackTop); + + // FIXME: Remove these if the last statement is a return statement. +#if USE(JSVALUE64) + JSValueRegs returnValueRegs(GPRInfo::returnValueGPR); +#else + JSValueRegs returnValueRegs(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR); +#endif + moveTrustedValue(jsUndefined(), returnValueRegs); + emitRestoreCalleeSaves(); + emitFunctionEpilogue(); + ret(); + + m_stackOverflow.link(this); + if (maxFrameExtentForSlowPathCall) + addPtr(TrustedImm32(-maxFrameExtentForSlowPathCall), stackPointerRegister); + setupArgumentsWithExecState(TrustedImmPtr(m_codeBlock)); + appendCallWithExceptionCheck(operationThrowStackOverflowError); + + // FIXME: Implement arity check. + Label arityCheck = label(); + emitFunctionPrologue(); + emitPutToCallFrameHeader(m_codeBlock, JSStack::CodeBlock); + jump(m_beginLabel); + + if (!m_divideErrorJumpList.empty()) { + m_divideErrorJumpList.link(this); + + setupArgumentsExecState(); + appendCallWithExceptionCheck(operationThrowDivideError); + } + + if (!m_outOfBoundsErrorJumpList.empty()) { + m_outOfBoundsErrorJumpList.link(this); + + setupArgumentsExecState(); + appendCallWithExceptionCheck(operationThrowOutOfBoundsAccessError); + } + + if (!m_exceptionChecks.empty()) { + m_exceptionChecks.link(this); + + copyCalleeSavesToVMCalleeSavesBuffer(); + + // lookupExceptionHandler is passed two arguments, the VM and the exec (the CallFrame*). + move(TrustedImmPtr(vm()), GPRInfo::argumentGPR0); + move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1); + +#if CPU(X86) + // FIXME: should use the call abstraction, but this is currently in the SpeculativeJIT layer! + poke(GPRInfo::argumentGPR0); + poke(GPRInfo::argumentGPR1, 1); +#endif + m_calls.append(std::make_pair(call(), FunctionPtr(lookupExceptionHandlerFromCallerFrame).value())); + jumpToExceptionHandler(); + } + + LinkBuffer patchBuffer(*m_vm, *this, m_codeBlock, JITCompilationMustSucceed); + + for (const auto& iterator : m_calls) + patchBuffer.link(iterator.first, FunctionPtr(iterator.second)); + + for (size_t i = 0; i < m_callCompilationInfo.size(); ++i) { + CallCompilationInfo& compilationInfo = m_callCompilationInfo[i]; + CallLinkInfo& info = *compilationInfo.callLinkInfo; + info.setCallLocations(patchBuffer.locationOfNearCall(compilationInfo.callReturnLocation), + patchBuffer.locationOf(compilationInfo.hotPathBegin), + patchBuffer.locationOfNearCall(compilationInfo.hotPathOther)); + } + + MacroAssemblerCodePtr withArityCheck = patchBuffer.locationOf(arityCheck); + CodeRef result = FINALIZE_CODE(patchBuffer, ("Baseline JIT code for WebAssembly")); + m_codeBlock->setJITCode(adoptRef(new DirectJITCode(result, withArityCheck, JITCode::BaselineJIT))); + m_codeBlock->capabilityLevel(); + } + + int buildSetLocal(WASMOpKind opKind, uint32_t localIndex, int, WASMType type) + { + switch (type) { + case WASMType::I32: + case WASMType::F32: + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); + store32(GPRInfo::regT0, localAddress(localIndex)); + break; + case WASMType::F64: + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, localAddress(localIndex)); + break; + default: + ASSERT_NOT_REACHED(); + } + if (opKind == WASMOpKind::Statement) + m_tempStackTop--; + return UNUSED; + } + + int buildSetGlobal(WASMOpKind opKind, uint32_t globalIndex, int, WASMType type) + { + move(TrustedImmPtr(&m_module->globalVariables()[globalIndex]), GPRInfo::regT0); + switch (type) { + case WASMType::I32: + case WASMType::F32: + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); + store32(GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMType::F64: + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, GPRInfo::regT0); + break; + default: + ASSERT_NOT_REACHED(); + } + if (opKind == WASMOpKind::Statement) + m_tempStackTop--; + return UNUSED; + } + + void buildReturn(int, WASMExpressionType returnType) + { +#if USE(JSVALUE64) + JSValueRegs returnValueRegs(GPRInfo::returnValueGPR); +#else + JSValueRegs returnValueRegs(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR); +#endif + switch (returnType) { + case WASMExpressionType::I32: + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::returnValueGPR); +#if USE(JSVALUE64) + or64(GPRInfo::tagTypeNumberRegister, GPRInfo::returnValueGPR); +#else + move(TrustedImm32(JSValue::Int32Tag), GPRInfo::returnValueGPR2); +#endif + m_tempStackTop--; + break; + case WASMExpressionType::F32: + case WASMExpressionType::F64: + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); + if (returnType == WASMExpressionType::F32) + convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + convertDoubleToValue(FPRInfo::fpRegT0, returnValueRegs); + m_tempStackTop--; + break; + case WASMExpressionType::Void: + moveTrustedValue(jsUndefined(), returnValueRegs); + break; + default: + ASSERT_NOT_REACHED(); + } + emitRestoreCalleeSaves(); + emitFunctionEpilogue(); + ret(); + } + + int buildImmediateI32(uint32_t immediate) + { + store32(Imm32(immediate), temporaryAddress(m_tempStackTop++)); + return UNUSED; + } + + int buildImmediateF32(float immediate) + { + store32(Imm32(bitwise_cast<int32_t>(immediate)), temporaryAddress(m_tempStackTop++)); + return UNUSED; + } + + int buildImmediateF64(double immediate) + { +#if USE(JSVALUE64) + store64(Imm64(bitwise_cast<int64_t>(immediate)), temporaryAddress(m_tempStackTop++)); +#else + union { + double doubleValue; + int32_t int32Values[2]; + } u = { immediate }; + m_tempStackTop++; + store32(Imm32(u.int32Values[0]), temporaryAddress(m_tempStackTop - 1)); + store32(Imm32(u.int32Values[1]), temporaryAddress(m_tempStackTop - 1).withOffset(4)); +#endif + return UNUSED; + } + + int buildGetLocal(uint32_t localIndex, WASMType type) + { + switch (type) { + case WASMType::I32: + case WASMType::F32: + load32(localAddress(localIndex), GPRInfo::regT0); + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop++)); + break; + case WASMType::F64: + loadDouble(localAddress(localIndex), FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++)); + break; + default: + ASSERT_NOT_REACHED(); + } + return UNUSED; + } + + int buildGetGlobal(uint32_t globalIndex, WASMType type) + { + move(TrustedImmPtr(&m_module->globalVariables()[globalIndex]), GPRInfo::regT0); + switch (type) { + case WASMType::I32: + case WASMType::F32: + load32(GPRInfo::regT0, GPRInfo::regT0); + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop++)); + break; + case WASMType::F64: + loadDouble(GPRInfo::regT0, FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++)); + break; + default: + ASSERT_NOT_REACHED(); + } + return UNUSED; + } + + int buildConvertType(int, WASMExpressionType fromType, WASMExpressionType toType, WASMTypeConversion conversion) + { + switch (fromType) { + case WASMExpressionType::I32: + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); + ASSERT(toType == WASMExpressionType::F32 || toType == WASMExpressionType::F64); + if (conversion == WASMTypeConversion::ConvertSigned) + convertInt32ToDouble(GPRInfo::regT0, FPRInfo::fpRegT0); + else { + ASSERT(conversion == WASMTypeConversion::ConvertUnsigned); +#if USE(JSVALUE64) + convertInt64ToDouble(GPRInfo::regT0, FPRInfo::fpRegT0); +#else + callOperation(operationConvertUnsignedInt32ToDouble, GPRInfo::regT0, FPRInfo::fpRegT0); +#endif + } + if (toType == WASMExpressionType::F32) + convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); + break; + case WASMExpressionType::F32: + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); + switch (toType) { + case WASMExpressionType::I32: + ASSERT(conversion == WASMTypeConversion::ConvertSigned); + convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + truncateDoubleToInt32(FPRInfo::fpRegT0, GPRInfo::regT0); + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); + break; + case WASMExpressionType::F64: + ASSERT(conversion == WASMTypeConversion::Promote); + convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); + break; + default: + ASSERT_NOT_REACHED(); + } + break; + case WASMExpressionType::F64: + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); + switch (toType) { + case WASMExpressionType::I32: + ASSERT(conversion == WASMTypeConversion::ConvertSigned); + truncateDoubleToInt32(FPRInfo::fpRegT0, GPRInfo::regT0); + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); + break; + case WASMExpressionType::F32: + ASSERT(conversion == WASMTypeConversion::Demote); + convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); + break; + default: + ASSERT_NOT_REACHED(); + } + break; + default: + ASSERT_NOT_REACHED(); + } + return UNUSED; + } + + int buildLoad(const MemoryAddress& memoryAddress, WASMExpressionType expressionType, WASMMemoryType memoryType, MemoryAccessConversion conversion) + { + const ArrayBuffer* arrayBuffer = m_module->arrayBuffer()->impl(); + move(TrustedImmPtr(arrayBuffer->data()), GPRInfo::regT0); + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); + if (memoryAddress.offset) + add32(TrustedImm32(memoryAddress.offset), GPRInfo::regT1, GPRInfo::regT1); + and32(TrustedImm32(~(sizeOfMemoryType(memoryType) - 1)), GPRInfo::regT1); + + ASSERT(arrayBuffer->byteLength() < (unsigned)(1 << 31)); + if (arrayBuffer->byteLength() >= sizeOfMemoryType(memoryType)) + m_outOfBoundsErrorJumpList.append(branch32(Above, GPRInfo::regT1, TrustedImm32(arrayBuffer->byteLength() - sizeOfMemoryType(memoryType)))); + else + m_outOfBoundsErrorJumpList.append(jump()); + + BaseIndex address = BaseIndex(GPRInfo::regT0, GPRInfo::regT1, TimesOne); + + switch (expressionType) { + case WASMExpressionType::I32: + switch (memoryType) { + case WASMMemoryType::I8: + if (conversion == MemoryAccessConversion::SignExtend) + load8SignedExtendTo32(address, GPRInfo::regT0); + else { + ASSERT(conversion == MemoryAccessConversion::ZeroExtend); + load8(address, GPRInfo::regT0); + } + break; + case WASMMemoryType::I16: + if (conversion == MemoryAccessConversion::SignExtend) + load16SignedExtendTo32(address, GPRInfo::regT0); + else { + ASSERT(conversion == MemoryAccessConversion::ZeroExtend); + load16(address, GPRInfo::regT0); + } + break; + case WASMMemoryType::I32: + load32(address, GPRInfo::regT0); + break; + default: + ASSERT_NOT_REACHED(); + } + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); + break; + case WASMExpressionType::F32: + ASSERT(memoryType == WASMMemoryType::F32 && conversion == MemoryAccessConversion::NoConversion); + load32(address, GPRInfo::regT0); + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); + break; + case WASMExpressionType::F64: + ASSERT(memoryType == WASMMemoryType::F64 && conversion == MemoryAccessConversion::NoConversion); + loadDouble(address, FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); + break; + default: + ASSERT_NOT_REACHED(); + } + return UNUSED; + } + + int buildStore(WASMOpKind opKind, const MemoryAddress& memoryAddress, WASMExpressionType expressionType, WASMMemoryType memoryType, int) + { + const ArrayBuffer* arrayBuffer = m_module->arrayBuffer()->impl(); + move(TrustedImmPtr(arrayBuffer->data()), GPRInfo::regT0); + load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT1); + if (memoryAddress.offset) + add32(TrustedImm32(memoryAddress.offset), GPRInfo::regT1, GPRInfo::regT1); + and32(TrustedImm32(~(sizeOfMemoryType(memoryType) - 1)), GPRInfo::regT1); + + ASSERT(arrayBuffer->byteLength() < (1u << 31)); + if (arrayBuffer->byteLength() >= sizeOfMemoryType(memoryType)) + m_outOfBoundsErrorJumpList.append(branch32(Above, GPRInfo::regT1, TrustedImm32(arrayBuffer->byteLength() - sizeOfMemoryType(memoryType)))); + else + m_outOfBoundsErrorJumpList.append(jump()); + + BaseIndex address = BaseIndex(GPRInfo::regT0, GPRInfo::regT1, TimesOne); + + switch (expressionType) { + case WASMExpressionType::I32: + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT2); + switch (memoryType) { + case WASMMemoryType::I8: + store8(GPRInfo::regT2, address); + break; + case WASMMemoryType::I16: + store16(GPRInfo::regT2, address); + break; + case WASMMemoryType::I32: + store32(GPRInfo::regT2, address); + break; + default: + ASSERT_NOT_REACHED(); + } + break; + case WASMExpressionType::F32: + ASSERT(memoryType == WASMMemoryType::F32); + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT2); + store32(GPRInfo::regT2, address); + break; + case WASMExpressionType::F64: + ASSERT(memoryType == WASMMemoryType::F64); + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, address); + break; + default: + ASSERT_NOT_REACHED(); + } + m_tempStackTop -= 2; + + if (opKind == WASMOpKind::Expression) { + switch (expressionType) { + case WASMExpressionType::I32: + case WASMExpressionType::F32: + store32(GPRInfo::regT2, temporaryAddress(m_tempStackTop++)); + break; + case WASMExpressionType::F64: + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++)); + break; + default: + ASSERT_NOT_REACHED(); + } + } + return UNUSED; + } + + int buildUnaryI32(int, WASMOpExpressionI32 op) + { + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); + switch (op) { + case WASMOpExpressionI32::Negate: + neg32(GPRInfo::regT0); + break; + case WASMOpExpressionI32::BitNot: + xor32(TrustedImm32(-1), GPRInfo::regT0); + break; + case WASMOpExpressionI32::CountLeadingZeros: + countLeadingZeros32(GPRInfo::regT0, GPRInfo::regT0); + break; + case WASMOpExpressionI32::LogicalNot: { + // FIXME: Don't use branches. + Jump zero = branchTest32(Zero, GPRInfo::regT0); + move(TrustedImm32(0), GPRInfo::regT0); + Jump end = jump(); + zero.link(this); + move(TrustedImm32(1), GPRInfo::regT0); + end.link(this); + break; + } + case WASMOpExpressionI32::Abs: { + // FIXME: Don't use branches. + Jump end = branchTest32(PositiveOrZero, GPRInfo::regT0); + neg32(GPRInfo::regT0); + end.link(this); + break; + } + default: + ASSERT_NOT_REACHED(); + } + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); + return UNUSED; + } + + int buildUnaryF32(int, WASMOpExpressionF32 op) + { + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); + switch (op) { + case WASMOpExpressionF32::Negate: + convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); + negateDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF32::Abs: + convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); + absDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF32::Ceil: + callOperation(ceilf, FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF32::Floor: + callOperation(floorf, FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF32::Sqrt: + convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); + sqrtDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + break; + default: + ASSERT_NOT_REACHED(); + } + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); + return UNUSED; + } + + int buildUnaryF64(int, WASMOpExpressionF64 op) + { + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); + switch (op) { + case WASMOpExpressionF64::Negate: + negateDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF64::Abs: + absDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF64::Sqrt: + sqrtDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF64::Ceil: + case WASMOpExpressionF64::Floor: + case WASMOpExpressionF64::Cos: + case WASMOpExpressionF64::Sin: + case WASMOpExpressionF64::Tan: + case WASMOpExpressionF64::ACos: + case WASMOpExpressionF64::ASin: + case WASMOpExpressionF64::ATan: + case WASMOpExpressionF64::Exp: + case WASMOpExpressionF64::Ln: + D_JITOperation_D operation; + switch (op) { + case WASMOpExpressionF64::Ceil: + operation = ceil; + break; + case WASMOpExpressionF64::Floor: + operation = floor; + break; + case WASMOpExpressionF64::Cos: + operation = cos; + break; + case WASMOpExpressionF64::Sin: + operation = sin; + break; + case WASMOpExpressionF64::Tan: + operation = tan; + break; + case WASMOpExpressionF64::ACos: + operation = acos; + break; + case WASMOpExpressionF64::ASin: + operation = asin; + break; + case WASMOpExpressionF64::ATan: + operation = atan; + break; + case WASMOpExpressionF64::Exp: + operation = exp; + break; + case WASMOpExpressionF64::Ln: + operation = log; + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + callOperation(operation, FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + default: + ASSERT_NOT_REACHED(); + } + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); + return UNUSED; + } + + int buildBinaryI32(int, int, WASMOpExpressionI32 op) + { + load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0); + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); + switch (op) { + case WASMOpExpressionI32::Add: + add32(GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::Sub: + sub32(GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::Mul: + mul32(GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::SDiv: + case WASMOpExpressionI32::UDiv: + case WASMOpExpressionI32::SMod: + case WASMOpExpressionI32::UMod: { + m_divideErrorJumpList.append(branchTest32(Zero, GPRInfo::regT1)); + if (op == WASMOpExpressionI32::SDiv || op == WASMOpExpressionI32::SMod) { + Jump denominatorNotNeg1 = branch32(NotEqual, GPRInfo::regT1, TrustedImm32(-1)); + m_divideErrorJumpList.append(branch32(Equal, GPRInfo::regT0, TrustedImm32(-2147483647-1))); + denominatorNotNeg1.link(this); + } +#if CPU(X86) || CPU(X86_64) + ASSERT(GPRInfo::regT0 == X86Registers::eax); + move(GPRInfo::regT1, X86Registers::ecx); + if (op == WASMOpExpressionI32::SDiv || op == WASMOpExpressionI32::SMod) { + x86ConvertToDoubleWord32(); + x86Div32(X86Registers::ecx); + } else { + ASSERT(op == WASMOpExpressionI32::UDiv || op == WASMOpExpressionI32::UMod); + xor32(X86Registers::edx, X86Registers::edx); + m_assembler.divl_r(X86Registers::ecx); + } + if (op == WASMOpExpressionI32::SMod || op == WASMOpExpressionI32::UMod) + move(X86Registers::edx, GPRInfo::regT0); +#else + // FIXME: We should be able to do an inline div on ARMv7 and ARM64. + switch (op) { + case WASMOpExpressionI32::SDiv: + callOperation(operationDiv, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::UDiv: + callOperation(operationUnsignedDiv, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::SMod: + callOperation(operationMod, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::UMod: + callOperation(operationUnsignedMod, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); + break; + default: + ASSERT_NOT_REACHED(); + } +#endif + break; + } + case WASMOpExpressionI32::BitOr: + or32(GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::BitAnd: + and32(GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::BitXor: + xor32(GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::LeftShift: + lshift32(GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::ArithmeticRightShift: + rshift32(GPRInfo::regT1, GPRInfo::regT0); + break; + case WASMOpExpressionI32::LogicalRightShift: + urshift32(GPRInfo::regT1, GPRInfo::regT0); + break; + default: + ASSERT_NOT_REACHED(); + } + m_tempStackTop--; + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); + return UNUSED; + } + + int buildBinaryF32(int, int, WASMOpExpressionF32 op) + { + loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); + convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); + switch (op) { + case WASMOpExpressionF32::Add: + addDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF32::Sub: + subDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF32::Mul: + mulDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF32::Div: + divDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + m_tempStackTop--; + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); + return UNUSED; + } + + int buildBinaryF64(int, int, WASMOpExpressionF64 op) + { + loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); + switch (op) { + case WASMOpExpressionF64::Add: + addDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF64::Sub: + subDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF64::Mul: + mulDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF64::Div: + divDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + case WASMOpExpressionF64::Mod: + case WASMOpExpressionF64::ATan2: + case WASMOpExpressionF64::Pow: + D_JITOperation_DD operation; + switch (op) { + case WASMOpExpressionF64::Mod: + operation = fmod; + break; + case WASMOpExpressionF64::ATan2: + operation = atan2; + break; + case WASMOpExpressionF64::Pow: + operation = pow; + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + callOperation(operation, FPRInfo::fpRegT0, FPRInfo::fpRegT1, FPRInfo::fpRegT0); + break; + default: + ASSERT_NOT_REACHED(); + } + m_tempStackTop--; + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop - 1)); + return UNUSED; + } + + int buildRelationalI32(int, int, WASMOpExpressionI32 op) + { + load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0); + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); + RelationalCondition condition; + switch (op) { + case WASMOpExpressionI32::EqualI32: + condition = Equal; + break; + case WASMOpExpressionI32::NotEqualI32: + condition = NotEqual; + break; + case WASMOpExpressionI32::SLessThanI32: + condition = LessThan; + break; + case WASMOpExpressionI32::ULessThanI32: + condition = Below; + break; + case WASMOpExpressionI32::SLessThanOrEqualI32: + condition = LessThanOrEqual; + break; + case WASMOpExpressionI32::ULessThanOrEqualI32: + condition = BelowOrEqual; + break; + case WASMOpExpressionI32::SGreaterThanI32: + condition = GreaterThan; + break; + case WASMOpExpressionI32::UGreaterThanI32: + condition = Above; + break; + case WASMOpExpressionI32::SGreaterThanOrEqualI32: + condition = GreaterThanOrEqual; + break; + case WASMOpExpressionI32::UGreaterThanOrEqualI32: + condition = AboveOrEqual; + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + compare32(condition, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT0); + m_tempStackTop--; + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop - 1)); + return UNUSED; + } + + int buildRelationalF32(int, int, WASMOpExpressionI32 op) + { + loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); + convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + convertFloatToDouble(FPRInfo::fpRegT1, FPRInfo::fpRegT1); + DoubleCondition condition; + switch (op) { + case WASMOpExpressionI32::EqualF32: + condition = DoubleEqual; + break; + case WASMOpExpressionI32::NotEqualF32: + condition = DoubleNotEqual; + break; + case WASMOpExpressionI32::LessThanF32: + condition = DoubleLessThan; + break; + case WASMOpExpressionI32::LessThanOrEqualF32: + condition = DoubleLessThanOrEqual; + break; + case WASMOpExpressionI32::GreaterThanF32: + condition = DoubleGreaterThan; + break; + case WASMOpExpressionI32::GreaterThanOrEqualF32: + condition = DoubleGreaterThanOrEqual; + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + m_tempStackTop--; + Jump trueCase = branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1); + store32(TrustedImm32(0), temporaryAddress(m_tempStackTop - 1)); + Jump end = jump(); + trueCase.link(this); + store32(TrustedImm32(1), temporaryAddress(m_tempStackTop - 1)); + end.link(this); + return UNUSED; + } + + int buildRelationalF64(int, int, WASMOpExpressionI32 op) + { + loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); + DoubleCondition condition; + switch (op) { + case WASMOpExpressionI32::EqualF64: + condition = DoubleEqual; + break; + case WASMOpExpressionI32::NotEqualF64: + condition = DoubleNotEqual; + break; + case WASMOpExpressionI32::LessThanF64: + condition = DoubleLessThan; + break; + case WASMOpExpressionI32::LessThanOrEqualF64: + condition = DoubleLessThanOrEqual; + break; + case WASMOpExpressionI32::GreaterThanF64: + condition = DoubleGreaterThan; + break; + case WASMOpExpressionI32::GreaterThanOrEqualF64: + condition = DoubleGreaterThanOrEqual; + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + m_tempStackTop--; + Jump trueCase = branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1); + store32(TrustedImm32(0), temporaryAddress(m_tempStackTop - 1)); + Jump end = jump(); + trueCase.link(this); + store32(TrustedImm32(1), temporaryAddress(m_tempStackTop - 1)); + end.link(this); + return UNUSED; + } + + int buildMinOrMaxI32(int, int, WASMOpExpressionI32 op) + { + load32(temporaryAddress(m_tempStackTop - 2), GPRInfo::regT0); + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); + RelationalCondition condition; + switch (op) { + case WASMOpExpressionI32::SMin: + condition = LessThanOrEqual; + break; + case WASMOpExpressionI32::UMin: + condition = BelowOrEqual; + break; + case WASMOpExpressionI32::SMax: + condition = GreaterThanOrEqual; + break; + case WASMOpExpressionI32::UMax: + condition = AboveOrEqual; + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + Jump useLeft = branch32(condition, GPRInfo::regT0, GPRInfo::regT1); + store32(GPRInfo::regT1, temporaryAddress(m_tempStackTop - 2)); + useLeft.link(this); + m_tempStackTop--; + return UNUSED; + } + + int buildMinOrMaxF64(int, int, WASMOpExpressionF64 op) + { + loadDouble(temporaryAddress(m_tempStackTop - 2), FPRInfo::fpRegT0); + loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT1); + DoubleCondition condition; + switch (op) { + case WASMOpExpressionF64::Min: + condition = DoubleLessThanOrEqual; + break; + case WASMOpExpressionF64::Max: + condition = DoubleGreaterThanOrEqual; + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + Jump useLeft = branchDouble(condition, FPRInfo::fpRegT0, FPRInfo::fpRegT1); + storeDouble(FPRInfo::fpRegT1, temporaryAddress(m_tempStackTop - 2)); + useLeft.link(this); + m_tempStackTop--; + return UNUSED; + } + + int buildCallInternal(uint32_t functionIndex, int, const WASMSignature& signature, WASMExpressionType returnType) + { + boxArgumentsAndAdjustStackPointer(signature.arguments); + + JSFunction* function = m_module->functions()[functionIndex].get(); + move(TrustedImmPtr(function), GPRInfo::regT0); + + callAndUnboxResult(returnType); + return UNUSED; + } + + int buildCallIndirect(uint32_t functionPointerTableIndex, int, int, const WASMSignature& signature, WASMExpressionType returnType) + { + boxArgumentsAndAdjustStackPointer(signature.arguments); + + const Vector<JSFunction*>& functions = m_module->functionPointerTables()[functionPointerTableIndex].functions; + move(TrustedImmPtr(functions.data()), GPRInfo::regT0); + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT1); + m_tempStackTop--; + and32(TrustedImm32(functions.size() - 1), GPRInfo::regT1); + loadPtr(BaseIndex(GPRInfo::regT0, GPRInfo::regT1, timesPtr()), GPRInfo::regT0); + + callAndUnboxResult(returnType); + return UNUSED; + } + + int buildCallImport(uint32_t functionImportIndex, int, const WASMSignature& signature, WASMExpressionType returnType) + { + boxArgumentsAndAdjustStackPointer(signature.arguments); + + JSFunction* function = m_module->importedFunctions()[functionImportIndex].get(); + move(TrustedImmPtr(function), GPRInfo::regT0); + + callAndUnboxResult(returnType); + return UNUSED; + } + + void appendExpressionList(int&, int) { } + + void discard(int) + { + m_tempStackTop--; + } + + void linkTarget(JumpTarget& target) + { + target.label = label(); + target.jumpList.link(this); + } + + void jumpToTarget(JumpTarget& target) + { + if (target.label.isSet()) + jump(target.label); + else + target.jumpList.append(jump()); + } + + void jumpToTargetIf(JumpCondition condition, int, JumpTarget& target) + { + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); + m_tempStackTop--; + Jump taken = branchTest32((condition == JumpCondition::Zero) ? Zero : NonZero, GPRInfo::regT0); + if (target.label.isSet()) + taken.linkTo(target.label, this); + else + target.jumpList.append(taken); + } + + void startLoop() + { + m_breakTargets.append(JumpTarget()); + m_continueTargets.append(JumpTarget()); + } + + void endLoop() + { + m_breakTargets.removeLast(); + m_continueTargets.removeLast(); + } + + void startSwitch() + { + m_breakTargets.append(JumpTarget()); + } + + void endSwitch() + { + m_breakTargets.removeLast(); + } + + void startLabel() + { + m_breakLabelTargets.append(JumpTarget()); + m_continueLabelTargets.append(JumpTarget()); + + linkTarget(m_continueLabelTargets.last()); + } + + void endLabel() + { + linkTarget(m_breakLabelTargets.last()); + + m_breakLabelTargets.removeLast(); + m_continueLabelTargets.removeLast(); + } + + JumpTarget& breakTarget() + { + return m_breakTargets.last(); + } + + JumpTarget& continueTarget() + { + return m_continueTargets.last(); + } + + JumpTarget& breakLabelTarget(uint32_t labelIndex) + { + return m_breakLabelTargets[labelIndex]; + } + + JumpTarget& continueLabelTarget(uint32_t labelIndex) + { + return m_continueLabelTargets[labelIndex]; + } + + void buildSwitch(int, const Vector<int64_t>& cases, Vector<JumpTarget>& targets, JumpTarget defaultTarget) + { + load32(temporaryAddress(m_tempStackTop - 1), GPRInfo::regT0); + m_tempStackTop--; + BinarySwitch binarySwitch(GPRInfo::regT0, cases, BinarySwitch::Int32); + while (binarySwitch.advance(*this)) { + unsigned index = binarySwitch.caseIndex(); + jump(targets[index].label); + } + binarySwitch.fallThrough().linkTo(defaultTarget.label, this); + } + +private: + union StackSlot { + int32_t intValue; + float floatValue; + double doubleValue; + }; + + enum class FloatingPointPrecision { Single, Double }; + + Address localAddress(unsigned localIndex) const + { + ASSERT(localIndex < m_numberOfLocals); + return Address(GPRInfo::callFrameRegister, -m_calleeSaveSpace - (localIndex + 1) * sizeof(StackSlot)); + } + + Address temporaryAddress(unsigned temporaryIndex) const + { + ASSERT(m_numberOfLocals + temporaryIndex < m_stackHeight); + return Address(GPRInfo::callFrameRegister, -m_calleeSaveSpace - (m_numberOfLocals + temporaryIndex + 1) * sizeof(StackSlot)); + } + + void appendCall(const FunctionPtr& function) + { + m_calls.append(std::make_pair(call(), function.value())); + } + + void appendCallWithExceptionCheck(const FunctionPtr& function) + { + appendCall(function); + m_exceptionChecks.append(emitExceptionCheck()); + } + + Call emitNakedCall(CodePtr function) + { + Call nakedCall = nearCall(); + m_calls.append(std::make_pair(nakedCall, function.executableAddress())); + return nakedCall; + } + + void appendCallSetResult(const FunctionPtr& function, GPRReg result) + { + appendCall(function); + move(GPRInfo::returnValueGPR, result); + } + +#if CPU(X86) + void appendCallSetResult(const FunctionPtr& function, FPRReg result, FloatingPointPrecision precision) + { + appendCall(function); + switch (precision) { + case FloatingPointPrecision::Single: + m_assembler.fstps(0, stackPointerRegister); + break; + case FloatingPointPrecision::Double: + m_assembler.fstpl(0, stackPointerRegister); + break; + } + loadDouble(stackPointerRegister, result); + } +#elif CPU(ARM) && !CPU(ARM_HARDFP) + void appendCallSetResult(const FunctionPtr& function, FPRReg result, FloatingPointPrecision) + { + appendCall(function); + m_assembler.vmov(result, GPRInfo::returnValueGPR, GPRInfo::returnValueGPR2); + } +#else // CPU(X86_64) || (CPU(ARM) && CPU(ARM_HARDFP)) || CPU(ARM64) || CPU(MIPS) || CPU(SH4) + void appendCallSetResult(const FunctionPtr& function, FPRReg result, FloatingPointPrecision) + { + appendCall(function); + moveDouble(FPRInfo::returnValueFPR, result); + } +#endif + +#if USE(JSVALUE64) + void callOperation(Z_JITOperation_EJ operation, GPRReg src, GPRReg dst) + { + setupArgumentsWithExecState(src); + appendCallSetResult(operation, dst); + } + + void callOperation(D_JITOperation_EJ operation, GPRReg src, FPRReg dst) + { + setupArgumentsWithExecState(src); + appendCallSetResult(operation, dst, FloatingPointPrecision::Double); + } +#else +// EncodedJSValue in JSVALUE32_64 is a 64-bit integer. When being compiled in ARM EABI, it must be aligned on an even-numbered register (r0, r2 or [sp]). +// To prevent the assembler from using wrong registers, let's occupy r1 or r3 with a dummy argument when necessary. +#if (COMPILER_SUPPORTS(EABI) && CPU(ARM)) || CPU(MIPS) +#define EABI_32BIT_DUMMY_ARG TrustedImm32(0), +#else +#define EABI_32BIT_DUMMY_ARG +#endif + + void callOperation(Z_JITOperation_EJ operation, GPRReg srcTag, GPRReg srcPayload, GPRReg dst) + { + setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG srcPayload, srcTag); + appendCallSetResult(operation, dst); + } + + void callOperation(D_JITOperation_EJ operation, GPRReg srcTag, GPRReg srcPayload, FPRReg dst) + { + setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG srcPayload, srcTag); + appendCallSetResult(operation, dst, FloatingPointPrecision::Double); + } +#endif + + void callOperation(float JIT_OPERATION (*operation)(float), FPRegisterID src, FPRegisterID dst) + { + setupArguments(src); + appendCallSetResult(operation, dst, FloatingPointPrecision::Single); + } + + void callOperation(D_JITOperation_D operation, FPRegisterID src, FPRegisterID dst) + { + setupArguments(src); + appendCallSetResult(operation, dst, FloatingPointPrecision::Double); + } + + void callOperation(int32_t JIT_OPERATION (*operation)(int32_t, int32_t), GPRReg src1, GPRReg src2, GPRReg dst) + { + setupArguments(src1, src2); + appendCallSetResult(operation, dst); + } + + void callOperation(uint32_t JIT_OPERATION (*operation)(uint32_t, uint32_t), GPRReg src1, GPRReg src2, GPRReg dst) + { + setupArguments(src1, src2); + appendCallSetResult(operation, dst); + } + + void callOperation(D_JITOperation_DD operation, FPRegisterID src1, FPRegisterID src2, FPRegisterID dst) + { + setupArguments(src1, src2); + appendCallSetResult(operation, dst, FloatingPointPrecision::Double); + } + + void callOperation(double JIT_OPERATION (*operation)(uint32_t), GPRReg src, FPRegisterID dst) + { + setupArguments(src); + appendCallSetResult(operation, dst, FloatingPointPrecision::Double); + } + + void boxArgumentsAndAdjustStackPointer(const Vector<WASMType>& arguments) + { + size_t argumentCount = arguments.size(); + int stackOffset = -m_calleeSaveSpace - WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_numberOfLocals + m_tempStackTop + argumentCount + 1 + JSStack::CallFrameHeaderSize); + + storeTrustedValue(jsUndefined(), Address(GPRInfo::callFrameRegister, (stackOffset + CallFrame::thisArgumentOffset()) * sizeof(Register))); + + for (size_t i = 0; i < argumentCount; ++i) { + Address address(GPRInfo::callFrameRegister, (stackOffset + CallFrame::argumentOffset(i)) * sizeof(Register)); +#if USE(JSVALUE64) + JSValueRegs valueRegs(GPRInfo::regT0); +#else + JSValueRegs valueRegs(GPRInfo::regT1, GPRInfo::regT0); +#endif + switch (arguments[i]) { + case WASMType::I32: + load32(temporaryAddress(m_tempStackTop - argumentCount + i), GPRInfo::regT0); +#if USE(JSVALUE64) + or64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0); + store64(GPRInfo::regT0, address); +#else + store32(GPRInfo::regT0, address.withOffset(PayloadOffset)); + store32(TrustedImm32(JSValue::Int32Tag), address.withOffset(TagOffset)); +#endif + break; + case WASMType::F32: + case WASMType::F64: + loadDouble(temporaryAddress(m_tempStackTop - argumentCount + i), FPRInfo::fpRegT0); + if (arguments[i] == WASMType::F32) + convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + convertDoubleToValue(FPRInfo::fpRegT0, valueRegs); + storeValue(valueRegs, address); + break; + default: + ASSERT_NOT_REACHED(); + } + } + m_tempStackTop -= argumentCount; + + addPtr(TrustedImm32(stackOffset * sizeof(Register) + sizeof(CallerFrameAndPC)), GPRInfo::callFrameRegister, stackPointerRegister); + store32(TrustedImm32(argumentCount + 1), Address(stackPointerRegister, JSStack::ArgumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); + } + + void callAndUnboxResult(WASMExpressionType returnType) + { + // regT0 holds callee. +#if USE(JSVALUE64) + store64(GPRInfo::regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) - sizeof(CallerFrameAndPC))); +#else + store32(GPRInfo::regT0, Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) + PayloadOffset - sizeof(CallerFrameAndPC))); + store32(TrustedImm32(JSValue::CellTag), Address(stackPointerRegister, JSStack::Callee * static_cast<int>(sizeof(Register)) + TagOffset - sizeof(CallerFrameAndPC))); +#endif + + DataLabelPtr addressOfLinkedFunctionCheck; + Jump slowCase = branchPtrWithPatch(NotEqual, GPRInfo::regT0, addressOfLinkedFunctionCheck, TrustedImmPtr(0)); + + CallLinkInfo* info = m_codeBlock->addCallLinkInfo(); + info->setUpCall(CallLinkInfo::Call, CodeOrigin(), GPRInfo::regT0); + m_callCompilationInfo.append(CallCompilationInfo()); + m_callCompilationInfo.last().hotPathBegin = addressOfLinkedFunctionCheck; + m_callCompilationInfo.last().callLinkInfo = info; + m_callCompilationInfo.last().hotPathOther = nearCall(); + Jump end = jump(); + + slowCase.link(this); + move(TrustedImmPtr(info), GPRInfo::regT2); + m_callCompilationInfo.last().callReturnLocation = emitNakedCall(m_vm->getCTIStub(linkCallThunkGenerator).code()); + + end.link(this); + addPtr(TrustedImm32(-m_calleeSaveSpace - WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_stackHeight) * sizeof(StackSlot) - maxFrameExtentForSlowPathCall), GPRInfo::callFrameRegister, stackPointerRegister); + checkStackPointerAlignment(); + + // FIXME: No need to do type conversion if the callee is a WebAssembly function. + // https://bugs.webkit.org/show_bug.cgi?id=149310 +#if USE(JSVALUE64) + JSValueRegs valueRegs(GPRInfo::returnValueGPR); +#else + JSValueRegs valueRegs(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR); +#endif + switch (returnType) { + case WASMExpressionType::I32: + convertValueToInt32(valueRegs, GPRInfo::regT0); + store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop++)); + break; + case WASMExpressionType::F32: + case WASMExpressionType::F64: +#if USE(JSVALUE64) + convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::nonPreservedNonReturnGPR); +#else + convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::nonPreservedNonReturnGPR, FPRInfo::fpRegT1); +#endif + if (returnType == WASMExpressionType::F32) + convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0); + storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++)); + break; + case WASMExpressionType::Void: + break; + default: + ASSERT_NOT_REACHED(); + } + } + +#if USE(JSVALUE64) + void convertValueToInt32(JSValueRegs valueRegs, GPRReg dst) + { + Jump checkJSInt32 = branchIfInt32(valueRegs); + + callOperation(operationConvertJSValueToInt32, valueRegs.gpr(), valueRegs.gpr()); + + checkJSInt32.link(this); + move(valueRegs.gpr(), dst); + } + + void convertValueToDouble(JSValueRegs valueRegs, FPRReg dst, GPRReg scratch) + { + Jump checkJSInt32 = branchIfInt32(valueRegs); + Jump checkJSNumber = branchIfNumber(valueRegs, scratch); + JumpList end; + + callOperation(operationConvertJSValueToDouble, valueRegs.gpr(), dst); + end.append(jump()); + + checkJSInt32.link(this); + convertInt32ToDouble(valueRegs.gpr(), dst); + end.append(jump()); + + checkJSNumber.link(this); + unboxDoubleWithoutAssertions(valueRegs.gpr(), dst); + end.link(this); + } +#else + void convertValueToInt32(JSValueRegs valueRegs, GPRReg dst) + { + Jump checkJSInt32 = branchIfInt32(valueRegs); + + callOperation(operationConvertJSValueToInt32, valueRegs.tagGPR(), valueRegs.payloadGPR(), valueRegs.payloadGPR()); + + checkJSInt32.link(this); + move(valueRegs.payloadGPR(), dst); + } + + void convertValueToDouble(JSValueRegs valueRegs, FPRReg dst, GPRReg scratch, FPRReg fpScratch) + { + Jump checkJSInt32 = branchIfInt32(valueRegs); + Jump checkJSNumber = branchIfNumber(valueRegs, scratch); + JumpList end; + + callOperation(operationConvertJSValueToDouble, valueRegs.tagGPR(), valueRegs.payloadGPR(), dst); + end.append(jump()); + + checkJSInt32.link(this); + convertInt32ToDouble(valueRegs.payloadGPR(), dst); + end.append(jump()); + + checkJSNumber.link(this); + unboxDouble(valueRegs.tagGPR(), valueRegs.payloadGPR(), dst, fpScratch); + end.link(this); + } +#endif + + void convertDoubleToValue(FPRReg fpr, JSValueRegs valueRegs) + { +#if USE(JSVALUE64) + boxDouble(fpr, valueRegs.gpr()); +#else + boxDouble(fpr, valueRegs.tagGPR(), valueRegs.payloadGPR()); +#endif + } + + JSWASMModule* m_module; + unsigned m_stackHeight; + unsigned m_numberOfLocals; + unsigned m_tempStackTop { 0 }; + unsigned m_calleeSaveSpace; + + Vector<JumpTarget> m_breakTargets; + Vector<JumpTarget> m_continueTargets; + Vector<JumpTarget> m_breakLabelTargets; + Vector<JumpTarget> m_continueLabelTargets; + + Label m_beginLabel; + Jump m_stackOverflow; + JumpList m_divideErrorJumpList; + JumpList m_outOfBoundsErrorJumpList; + JumpList m_exceptionChecks; + + Vector<std::pair<Call, void*>> m_calls; + Vector<CallCompilationInfo> m_callCompilationInfo; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // WASMFunctionCompiler_h diff --git a/Source/JavaScriptCore/wasm/WASMFunctionParser.cpp b/Source/JavaScriptCore/wasm/WASMFunctionParser.cpp new file mode 100644 index 000000000..e65da5885 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMFunctionParser.cpp @@ -0,0 +1,1225 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WASMFunctionParser.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSCJSValueInlines.h" +#include "JSWASMModule.h" +#include "WASMFunctionCompiler.h" +#include "WASMFunctionB3IRGenerator.h" +#include "WASMFunctionSyntaxChecker.h" + +#define PROPAGATE_ERROR() do { if (!m_errorMessage.isNull()) return 0; } while (0) +#define FAIL_WITH_MESSAGE(errorMessage) do { m_errorMessage = errorMessage; return 0; } while (0) +#define READ_FLOAT_OR_FAIL(result, errorMessage) do { if (!m_reader.readFloat(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_DOUBLE_OR_FAIL(result, errorMessage) do { if (!m_reader.readDouble(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_COMPACT_INT32_OR_FAIL(result, errorMessage) do { if (!m_reader.readCompactInt32(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_COMPACT_UINT32_OR_FAIL(result, errorMessage) do { if (!m_reader.readCompactUInt32(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_EXPRESSION_TYPE_OR_FAIL(result, errorMessage) do { if (!m_reader.readExpressionType(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_OP_STATEMENT_OR_FAIL(hasImmediate, op, opWithImmediate, immediate, errorMessage) do { if (!m_reader.readOpStatement(hasImmediate, op, opWithImmediate, immediate)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_OP_EXPRESSION_I32_OR_FAIL(hasImmediate, op, opWithImmediate, immediate, errorMessage) do { if (!m_reader.readOpExpressionI32(hasImmediate, op, opWithImmediate, immediate)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_OP_EXPRESSION_F32_OR_FAIL(hasImmediate, op, opWithImmediate, immediate, errorMessage) do { if (!m_reader.readOpExpressionF32(hasImmediate, op, opWithImmediate, immediate)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_OP_EXPRESSION_F64_OR_FAIL(hasImmediate, op, opWithImmediate, immediate, errorMessage) do { if (!m_reader.readOpExpressionF64(hasImmediate, op, opWithImmediate, immediate)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_OP_EXPRESSION_VOID_OR_FAIL(op, errorMessage) do { if (!m_reader.readOpExpressionVoid(op)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_VARIABLE_TYPES_OR_FAIL(hasImmediate, variableTypes, variableTypesWithImmediate, immediate, errorMessage) do { if (!m_reader.readVariableTypes(hasImmediate, variableTypes, variableTypesWithImmediate, immediate)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_SWITCH_CASE_OR_FAIL(result, errorMessage) do { if (!m_reader.readSwitchCase(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define FAIL_IF_FALSE(condition, errorMessage) do { if (!(condition)) FAIL_WITH_MESSAGE(errorMessage); } while (0) + +#define UNUSED 0 + +namespace JSC { + +static String nameOfType(WASMType type) +{ + switch (type) { + case WASMType::I32: + return "int32"; + case WASMType::F32: + return "float32"; + case WASMType::F64: + return "float64"; + default: + RELEASE_ASSERT_NOT_REACHED(); + } +} + +bool WASMFunctionParser::checkSyntax(JSWASMModule* module, const SourceCode& source, size_t functionIndex, unsigned startOffsetInSource, unsigned& endOffsetInSource, unsigned& stackHeight, String& errorMessage) +{ + WASMFunctionParser parser(module, source, functionIndex); + WASMFunctionSyntaxChecker syntaxChecker; + parser.m_reader.setOffset(startOffsetInSource); + parser.parseFunction(syntaxChecker); + if (!parser.m_errorMessage.isNull()) { + errorMessage = parser.m_errorMessage; + return false; + } + endOffsetInSource = parser.m_reader.offset(); + stackHeight = syntaxChecker.stackHeight(); + return true; +} + +void WASMFunctionParser::compile(VM& vm, CodeBlock* codeBlock, JSWASMModule* module, const SourceCode& source, size_t functionIndex) +{ + WASMFunctionParser parser(module, source, functionIndex); + WASMFunctionCompiler compiler(vm, codeBlock, module, module->functionStackHeights()[functionIndex]); + parser.m_reader.setOffset(module->functionStartOffsetsInSource()[functionIndex]); + parser.parseFunction(compiler); + ASSERT(parser.m_errorMessage.isNull()); +} + +template <class Context> +bool WASMFunctionParser::parseFunction(Context& context) +{ + const WASMSignature& signature = m_module->signatures()[m_module->functionDeclarations()[m_functionIndex].signatureIndex]; + + m_returnType = signature.returnType; + + parseLocalVariables(); + PROPAGATE_ERROR(); + + const Vector<WASMType>& arguments = signature.arguments; + for (size_t i = 0; i < arguments.size(); ++i) + m_localTypes.append(arguments[i]); + for (uint32_t i = 0; i < m_numberOfI32LocalVariables; ++i) + m_localTypes.append(WASMType::I32); + for (uint32_t i = 0; i < m_numberOfF32LocalVariables; ++i) + m_localTypes.append(WASMType::F32); + for (uint32_t i = 0; i < m_numberOfF64LocalVariables; ++i) + m_localTypes.append(WASMType::F64); + + context.startFunction(arguments, m_numberOfI32LocalVariables, m_numberOfF32LocalVariables, m_numberOfF64LocalVariables); + + parseBlockStatement(context); + PROPAGATE_ERROR(); + + context.endFunction(); + return true; +} + +bool WASMFunctionParser::parseLocalVariables() +{ + m_numberOfI32LocalVariables = 0; + m_numberOfF32LocalVariables = 0; + m_numberOfF64LocalVariables = 0; + + bool hasImmediate; + WASMVariableTypes variableTypes; + WASMVariableTypesWithImmediate variableTypesWithImmediate; + uint8_t immediate; + READ_VARIABLE_TYPES_OR_FAIL(hasImmediate, variableTypes, variableTypesWithImmediate, immediate, "Cannot read the types of local variables."); + if (!hasImmediate) { + if (static_cast<uint8_t>(variableTypes) & static_cast<uint8_t>(WASMVariableTypes::I32)) + READ_COMPACT_UINT32_OR_FAIL(m_numberOfI32LocalVariables, "Cannot read the number of int32 local variables."); + if (static_cast<uint8_t>(variableTypes) & static_cast<uint8_t>(WASMVariableTypes::F32)) + READ_COMPACT_UINT32_OR_FAIL(m_numberOfF32LocalVariables, "Cannot read the number of float32 local variables."); + if (static_cast<uint8_t>(variableTypes) & static_cast<uint8_t>(WASMVariableTypes::F64)) + READ_COMPACT_UINT32_OR_FAIL(m_numberOfF64LocalVariables, "Cannot read the number of float64 local variables."); + } else + m_numberOfI32LocalVariables = immediate; + return true; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseStatement(Context& context) +{ + bool hasImmediate; + WASMOpStatement op; + WASMOpStatementWithImmediate opWithImmediate; + uint8_t immediate; + READ_OP_STATEMENT_OR_FAIL(hasImmediate, op, opWithImmediate, immediate, "Cannot read the statement opcode."); + if (!hasImmediate) { + switch (op) { + case WASMOpStatement::SetLocal: + parseSetLocal(context, WASMOpKind::Statement, WASMExpressionType::Void); + break; + case WASMOpStatement::SetGlobal: + parseSetGlobal(context, WASMOpKind::Statement, WASMExpressionType::Void); + break; + case WASMOpStatement::I32Store8: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::NoOffset); + break; + case WASMOpStatement::I32StoreWithOffset8: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::WithOffset); + break; + case WASMOpStatement::I32Store16: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::NoOffset); + break; + case WASMOpStatement::I32StoreWithOffset16: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::WithOffset); + break; + case WASMOpStatement::I32Store32: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::NoOffset); + break; + case WASMOpStatement::I32StoreWithOffset32: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::WithOffset); + break; + case WASMOpStatement::F32Store: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::NoOffset); + break; + case WASMOpStatement::F32StoreWithOffset: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::WithOffset); + break; + case WASMOpStatement::F64Store: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::NoOffset); + break; + case WASMOpStatement::F64StoreWithOffset: + parseStore(context, WASMOpKind::Statement, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::WithOffset); + break; + case WASMOpStatement::CallInternal: + parseCallInternal(context, WASMOpKind::Statement, WASMExpressionType::Void); + break; + case WASMOpStatement::CallIndirect: + parseCallIndirect(context, WASMOpKind::Statement, WASMExpressionType::Void); + break; + case WASMOpStatement::CallImport: + parseCallImport(context, WASMOpKind::Statement, WASMExpressionType::Void); + break; + case WASMOpStatement::Return: + parseReturnStatement(context); + break; + case WASMOpStatement::Block: + parseBlockStatement(context); + break; + case WASMOpStatement::If: + parseIfStatement(context); + break; + case WASMOpStatement::IfElse: + parseIfElseStatement(context); + break; + case WASMOpStatement::While: + parseWhileStatement(context); + break; + case WASMOpStatement::Do: + parseDoStatement(context); + break; + case WASMOpStatement::Label: + parseLabelStatement(context); + break; + case WASMOpStatement::Break: + parseBreakStatement(context); + break; + case WASMOpStatement::BreakLabel: + parseBreakLabelStatement(context); + break; + case WASMOpStatement::Continue: + parseContinueStatement(context); + break; + case WASMOpStatement::ContinueLabel: + parseContinueLabelStatement(context); + break; + case WASMOpStatement::Switch: + parseSwitchStatement(context); + break; + default: + ASSERT_NOT_REACHED(); + } + } else { + switch (opWithImmediate) { + case WASMOpStatementWithImmediate::SetLocal: + parseSetLocal(context, WASMOpKind::Statement, WASMExpressionType::Void, immediate); + break; + case WASMOpStatementWithImmediate::SetGlobal: + parseSetGlobal(context, WASMOpKind::Statement, WASMExpressionType::Void, immediate); + break; + default: + ASSERT_NOT_REACHED(); + } + } + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseReturnStatement(Context& context) +{ + ContextExpression expression = 0; + if (m_returnType != WASMExpressionType::Void) { + expression = parseExpression(context, m_returnType); + PROPAGATE_ERROR(); + } + context.buildReturn(expression, m_returnType); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseBlockStatement(Context& context) +{ + uint32_t numberOfStatements; + READ_COMPACT_UINT32_OR_FAIL(numberOfStatements, "Cannot read the number of statements."); + for (uint32_t i = 0; i < numberOfStatements; ++i) { + parseStatement(context); + PROPAGATE_ERROR(); + } + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseIfStatement(Context& context) +{ + ContextJumpTarget end; + + ContextExpression expression = parseExpressionI32(context); + PROPAGATE_ERROR(); + + context.jumpToTargetIf(Context::JumpCondition::Zero, expression, end); + + parseStatement(context); + PROPAGATE_ERROR(); + + context.linkTarget(end); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseIfElseStatement(Context& context) +{ + ContextJumpTarget elseTarget; + ContextJumpTarget end; + + ContextExpression expression = parseExpressionI32(context); + PROPAGATE_ERROR(); + + context.jumpToTargetIf(Context::JumpCondition::Zero, expression, elseTarget); + + parseStatement(context); + PROPAGATE_ERROR(); + + context.jumpToTarget(end); + context.linkTarget(elseTarget); + + parseStatement(context); + PROPAGATE_ERROR(); + + context.linkTarget(end); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseWhileStatement(Context& context) +{ + context.startLoop(); + context.linkTarget(context.continueTarget()); + + ContextExpression expression = parseExpressionI32(context); + PROPAGATE_ERROR(); + + context.jumpToTargetIf(Context::JumpCondition::Zero, expression, context.breakTarget()); + + m_breakScopeDepth++; + m_continueScopeDepth++; + parseStatement(context); + PROPAGATE_ERROR(); + m_continueScopeDepth--; + m_breakScopeDepth--; + + context.jumpToTarget(context.continueTarget()); + + context.linkTarget(context.breakTarget()); + context.endLoop(); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseDoStatement(Context& context) +{ + context.startLoop(); + + ContextJumpTarget topOfLoop; + context.linkTarget(topOfLoop); + + m_breakScopeDepth++; + m_continueScopeDepth++; + parseStatement(context); + PROPAGATE_ERROR(); + m_continueScopeDepth--; + m_breakScopeDepth--; + + context.linkTarget(context.continueTarget()); + + ContextExpression expression = parseExpressionI32(context); + PROPAGATE_ERROR(); + + context.jumpToTargetIf(Context::JumpCondition::NonZero, expression, topOfLoop); + + context.linkTarget(context.breakTarget()); + context.endLoop(); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseLabelStatement(Context& context) +{ + context.startLabel(); + m_labelDepth++; + parseStatement(context); + PROPAGATE_ERROR(); + m_labelDepth--; + context.endLabel(); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseBreakStatement(Context& context) +{ + FAIL_IF_FALSE(m_breakScopeDepth, "'break' is only valid inside a switch or loop statement."); + context.jumpToTarget(context.breakTarget()); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseBreakLabelStatement(Context& context) +{ + uint32_t labelIndex; + READ_COMPACT_UINT32_OR_FAIL(labelIndex, "Cannot read the label index."); + FAIL_IF_FALSE(labelIndex < m_labelDepth, "The label index is incorrect."); + context.jumpToTarget(context.breakLabelTarget(labelIndex)); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseContinueStatement(Context& context) +{ + FAIL_IF_FALSE(m_continueScopeDepth, "'continue' is only valid inside a loop statement."); + context.jumpToTarget(context.continueTarget()); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseContinueLabelStatement(Context& context) +{ + uint32_t labelIndex; + READ_COMPACT_UINT32_OR_FAIL(labelIndex, "Cannot read the label index."); + FAIL_IF_FALSE(labelIndex < m_labelDepth, "The label index is incorrect."); + context.jumpToTarget(context.continueLabelTarget(labelIndex)); + return UNUSED; +} + +template <class Context> +ContextStatement WASMFunctionParser::parseSwitchStatement(Context& context) +{ + context.startSwitch(); + uint32_t numberOfCases; + READ_COMPACT_UINT32_OR_FAIL(numberOfCases, "Cannot read the number of cases."); + ContextExpression expression = parseExpressionI32(context); + PROPAGATE_ERROR(); + + ContextJumpTarget compare; + context.jumpToTarget(compare); + + Vector<int64_t> cases; + Vector<ContextJumpTarget> targets; + cases.reserveInitialCapacity(numberOfCases); + targets.reserveInitialCapacity(numberOfCases); + bool hasDefault = false; + ContextJumpTarget defaultTarget; + + m_breakScopeDepth++; + for (uint32_t i = 0; i < numberOfCases; ++i) { + WASMSwitchCase switchCase; + READ_SWITCH_CASE_OR_FAIL(switchCase, "Cannot read the switch case."); + switch (switchCase) { + case WASMSwitchCase::CaseWithNoStatements: + case WASMSwitchCase::CaseWithStatement: + case WASMSwitchCase::CaseWithBlockStatement: { + uint32_t value; + READ_COMPACT_INT32_OR_FAIL(value, "Cannot read the value of the switch case."); + cases.uncheckedAppend(value); + ContextJumpTarget target; + context.linkTarget(target); + targets.uncheckedAppend(target); + if (switchCase == WASMSwitchCase::CaseWithStatement) { + parseStatement(context); + PROPAGATE_ERROR(); + } else if (switchCase == WASMSwitchCase::CaseWithBlockStatement) { + parseBlockStatement(context); + PROPAGATE_ERROR(); + } + break; + } + case WASMSwitchCase::DefaultWithNoStatements: + case WASMSwitchCase::DefaultWithStatement: + case WASMSwitchCase::DefaultWithBlockStatement: { + FAIL_IF_FALSE(i == numberOfCases - 1, "The default case must be the last case."); + hasDefault = true; + context.linkTarget(defaultTarget); + if (switchCase == WASMSwitchCase::DefaultWithStatement) { + parseStatement(context); + PROPAGATE_ERROR(); + } else if (switchCase == WASMSwitchCase::DefaultWithBlockStatement) { + parseBlockStatement(context); + PROPAGATE_ERROR(); + } + break; + } + default: + ASSERT_NOT_REACHED(); + } + } + if (!hasDefault) + context.linkTarget(defaultTarget); + + m_breakScopeDepth--; + + context.jumpToTarget(context.breakTarget()); + context.linkTarget(compare); + + context.buildSwitch(expression, cases, targets, defaultTarget); + + context.linkTarget(context.breakTarget()); + context.endSwitch(); + return UNUSED; +} + +template <class Context> +ContextExpression WASMFunctionParser::parseExpression(Context& context, WASMExpressionType expressionType) +{ + switch (expressionType) { + case WASMExpressionType::I32: + return parseExpressionI32(context); + case WASMExpressionType::F32: + return parseExpressionF32(context); + case WASMExpressionType::F64: + return parseExpressionF64(context); + case WASMExpressionType::Void: + return parseExpressionVoid(context); + default: + RELEASE_ASSERT_NOT_REACHED(); + } +} + +template <class Context> +ContextExpression WASMFunctionParser::parseExpressionI32(Context& context) +{ + bool hasImmediate; + WASMOpExpressionI32 op; + WASMOpExpressionI32WithImmediate opWithImmediate; + uint8_t immediate; + READ_OP_EXPRESSION_I32_OR_FAIL(hasImmediate, op, opWithImmediate, immediate, "Cannot read the int32 expression opcode."); + if (!hasImmediate) { + switch (op) { + case WASMOpExpressionI32::ConstantPoolIndex: + return parseConstantPoolIndexExpressionI32(context); + case WASMOpExpressionI32::Immediate: + return parseImmediateExpressionI32(context); + case WASMOpExpressionI32::GetLocal: + return parseGetLocalExpression(context, WASMType::I32); + case WASMOpExpressionI32::GetGlobal: + return parseGetGlobalExpression(context, WASMType::I32); + case WASMOpExpressionI32::SetLocal: + return parseSetLocal(context, WASMOpKind::Expression, WASMExpressionType::I32); + case WASMOpExpressionI32::SetGlobal: + return parseSetGlobal(context, WASMOpKind::Expression, WASMExpressionType::I32); + case WASMOpExpressionI32::SLoad8: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::NoOffset, MemoryAccessConversion::SignExtend); + case WASMOpExpressionI32::SLoadWithOffset8: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::WithOffset, MemoryAccessConversion::SignExtend); + case WASMOpExpressionI32::ULoad8: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::NoOffset, MemoryAccessConversion::ZeroExtend); + case WASMOpExpressionI32::ULoadWithOffset8: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::WithOffset, MemoryAccessConversion::ZeroExtend); + case WASMOpExpressionI32::SLoad16: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::NoOffset, MemoryAccessConversion::SignExtend); + case WASMOpExpressionI32::SLoadWithOffset16: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::WithOffset, MemoryAccessConversion::SignExtend); + case WASMOpExpressionI32::ULoad16: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::NoOffset, MemoryAccessConversion::ZeroExtend); + case WASMOpExpressionI32::ULoadWithOffset16: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::WithOffset, MemoryAccessConversion::ZeroExtend); + case WASMOpExpressionI32::Load32: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::NoOffset); + case WASMOpExpressionI32::LoadWithOffset32: + return parseLoad(context, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::WithOffset); + case WASMOpExpressionI32::Store8: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::NoOffset); + case WASMOpExpressionI32::StoreWithOffset8: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::I32, WASMMemoryType::I8, MemoryAccessOffsetMode::WithOffset); + case WASMOpExpressionI32::Store16: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::NoOffset); + case WASMOpExpressionI32::StoreWithOffset16: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::I32, WASMMemoryType::I16, MemoryAccessOffsetMode::WithOffset); + case WASMOpExpressionI32::Store32: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::NoOffset); + case WASMOpExpressionI32::StoreWithOffset32: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::I32, WASMMemoryType::I32, MemoryAccessOffsetMode::WithOffset); + case WASMOpExpressionI32::CallInternal: + return parseCallInternal(context, WASMOpKind::Expression, WASMExpressionType::I32); + case WASMOpExpressionI32::CallIndirect: + return parseCallIndirect(context, WASMOpKind::Expression, WASMExpressionType::I32); + case WASMOpExpressionI32::CallImport: + return parseCallImport(context, WASMOpKind::Expression, WASMExpressionType::I32); + case WASMOpExpressionI32::Conditional: + return parseConditional(context, WASMExpressionType::I32); + case WASMOpExpressionI32::Comma: + return parseComma(context, WASMExpressionType::I32); + case WASMOpExpressionI32::FromF32: + return parseConvertType(context, WASMExpressionType::F32, WASMExpressionType::I32, WASMTypeConversion::ConvertSigned); + case WASMOpExpressionI32::FromF64: + return parseConvertType(context, WASMExpressionType::F64, WASMExpressionType::I32, WASMTypeConversion::ConvertSigned); + case WASMOpExpressionI32::Negate: + case WASMOpExpressionI32::BitNot: + case WASMOpExpressionI32::CountLeadingZeros: + case WASMOpExpressionI32::LogicalNot: + case WASMOpExpressionI32::Abs: + return parseUnaryExpressionI32(context, op); + case WASMOpExpressionI32::Add: + case WASMOpExpressionI32::Sub: + case WASMOpExpressionI32::Mul: + case WASMOpExpressionI32::SDiv: + case WASMOpExpressionI32::UDiv: + case WASMOpExpressionI32::SMod: + case WASMOpExpressionI32::UMod: + case WASMOpExpressionI32::BitOr: + case WASMOpExpressionI32::BitAnd: + case WASMOpExpressionI32::BitXor: + case WASMOpExpressionI32::LeftShift: + case WASMOpExpressionI32::ArithmeticRightShift: + case WASMOpExpressionI32::LogicalRightShift: + return parseBinaryExpressionI32(context, op); + case WASMOpExpressionI32::EqualI32: + case WASMOpExpressionI32::NotEqualI32: + case WASMOpExpressionI32::SLessThanI32: + case WASMOpExpressionI32::ULessThanI32: + case WASMOpExpressionI32::SLessThanOrEqualI32: + case WASMOpExpressionI32::ULessThanOrEqualI32: + case WASMOpExpressionI32::SGreaterThanI32: + case WASMOpExpressionI32::UGreaterThanI32: + case WASMOpExpressionI32::SGreaterThanOrEqualI32: + case WASMOpExpressionI32::UGreaterThanOrEqualI32: + return parseRelationalI32ExpressionI32(context, op); + case WASMOpExpressionI32::EqualF32: + case WASMOpExpressionI32::NotEqualF32: + case WASMOpExpressionI32::LessThanF32: + case WASMOpExpressionI32::LessThanOrEqualF32: + case WASMOpExpressionI32::GreaterThanF32: + case WASMOpExpressionI32::GreaterThanOrEqualF32: + return parseRelationalF32ExpressionI32(context, op); + case WASMOpExpressionI32::EqualF64: + case WASMOpExpressionI32::NotEqualF64: + case WASMOpExpressionI32::LessThanF64: + case WASMOpExpressionI32::LessThanOrEqualF64: + case WASMOpExpressionI32::GreaterThanF64: + case WASMOpExpressionI32::GreaterThanOrEqualF64: + return parseRelationalF64ExpressionI32(context, op); + case WASMOpExpressionI32::SMin: + case WASMOpExpressionI32::UMin: + case WASMOpExpressionI32::SMax: + case WASMOpExpressionI32::UMax: + return parseMinOrMaxExpressionI32(context, op); + default: + ASSERT_NOT_REACHED(); + } + } else { + switch (opWithImmediate) { + case WASMOpExpressionI32WithImmediate::ConstantPoolIndex: + return parseConstantPoolIndexExpressionI32(context, immediate); + case WASMOpExpressionI32WithImmediate::Immediate: + return parseImmediateExpressionI32(context, immediate); + case WASMOpExpressionI32WithImmediate::GetLocal: + return parseGetLocalExpression(context, WASMType::I32, immediate); + default: + ASSERT_NOT_REACHED(); + } + } + return 0; +} + +template <class Context> +ContextExpression WASMFunctionParser::parseConstantPoolIndexExpressionI32(Context& context, uint32_t constantPoolIndex) +{ + FAIL_IF_FALSE(constantPoolIndex < m_module->i32Constants().size(), "The constant pool index is incorrect."); + return context.buildImmediateI32(m_module->i32Constants()[constantPoolIndex]); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseConstantPoolIndexExpressionI32(Context& context) +{ + uint32_t constantPoolIndex; + READ_COMPACT_UINT32_OR_FAIL(constantPoolIndex, "Cannot read the constant pool index."); + return parseConstantPoolIndexExpressionI32(context, constantPoolIndex); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseImmediateExpressionI32(Context& context, uint32_t immediate) +{ + return context.buildImmediateI32(immediate); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseImmediateExpressionI32(Context& context) +{ + uint32_t immediate; + READ_COMPACT_UINT32_OR_FAIL(immediate, "Cannot read the immediate."); + return parseImmediateExpressionI32(context, immediate); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseUnaryExpressionI32(Context& context, WASMOpExpressionI32 op) +{ + ContextExpression expression = parseExpressionI32(context); + PROPAGATE_ERROR(); + return context.buildUnaryI32(expression, op); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseBinaryExpressionI32(Context& context, WASMOpExpressionI32 op) +{ + ContextExpression left = parseExpressionI32(context); + PROPAGATE_ERROR(); + ContextExpression right = parseExpressionI32(context); + PROPAGATE_ERROR(); + return context.buildBinaryI32(left, right, op); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseRelationalI32ExpressionI32(Context& context, WASMOpExpressionI32 op) +{ + ContextExpression left = parseExpressionI32(context); + PROPAGATE_ERROR(); + ContextExpression right = parseExpressionI32(context); + PROPAGATE_ERROR(); + return context.buildRelationalI32(left, right, op); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseRelationalF32ExpressionI32(Context& context, WASMOpExpressionI32 op) +{ + ContextExpression left = parseExpressionF32(context); + PROPAGATE_ERROR(); + ContextExpression right = parseExpressionF32(context); + PROPAGATE_ERROR(); + return context.buildRelationalF32(left, right, op); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseRelationalF64ExpressionI32(Context& context, WASMOpExpressionI32 op) +{ + ContextExpression left = parseExpressionF64(context); + PROPAGATE_ERROR(); + ContextExpression right = parseExpressionF64(context); + PROPAGATE_ERROR(); + return context.buildRelationalF64(left, right, op); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseMinOrMaxExpressionI32(Context& context, WASMOpExpressionI32 op) +{ + uint32_t numberOfArguments; + READ_COMPACT_UINT32_OR_FAIL(numberOfArguments, "Cannot read the number of arguments to min/max."); + FAIL_IF_FALSE(numberOfArguments >= 2, "Min/max must be passed at least 2 arguments."); + ContextExpression current = parseExpressionI32(context); + PROPAGATE_ERROR(); + for (uint32_t i = 1; i < numberOfArguments; ++i) { + ContextExpression expression = parseExpressionI32(context); + PROPAGATE_ERROR(); + current = context.buildMinOrMaxI32(current, expression, op); + } + return current; +} + +template <class Context> +ContextExpression WASMFunctionParser::parseExpressionF32(Context& context) +{ + bool hasImmediate; + WASMOpExpressionF32 op; + WASMOpExpressionF32WithImmediate opWithImmediate; + uint8_t immediate; + READ_OP_EXPRESSION_F32_OR_FAIL(hasImmediate, op, opWithImmediate, immediate, "Cannot read the float32 expression opcode."); + if (!hasImmediate) { + switch (op) { + case WASMOpExpressionF32::ConstantPoolIndex: + return parseConstantPoolIndexExpressionF32(context); + case WASMOpExpressionF32::Immediate: + return parseImmediateExpressionF32(context); + case WASMOpExpressionF32::GetLocal: + return parseGetLocalExpression(context, WASMType::F32); + case WASMOpExpressionF32::GetGlobal: + return parseGetGlobalExpression(context, WASMType::F32); + case WASMOpExpressionF32::SetLocal: + return parseSetLocal(context, WASMOpKind::Expression, WASMExpressionType::F32); + case WASMOpExpressionF32::SetGlobal: + return parseSetGlobal(context, WASMOpKind::Expression, WASMExpressionType::F32); + case WASMOpExpressionF32::Load: + return parseLoad(context, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::NoOffset); + case WASMOpExpressionF32::LoadWithOffset: + return parseLoad(context, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::WithOffset); + case WASMOpExpressionF32::Store: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::NoOffset); + case WASMOpExpressionF32::StoreWithOffset: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::F32, WASMMemoryType::F32, MemoryAccessOffsetMode::WithOffset); + case WASMOpExpressionF32::CallInternal: + return parseCallInternal(context, WASMOpKind::Expression, WASMExpressionType::F32); + case WASMOpExpressionF32::CallIndirect: + return parseCallIndirect(context, WASMOpKind::Expression, WASMExpressionType::F32); + case WASMOpExpressionF32::Conditional: + return parseConditional(context, WASMExpressionType::F32); + case WASMOpExpressionF32::Comma: + return parseComma(context, WASMExpressionType::F32); + case WASMOpExpressionF32::FromS32: + return parseConvertType(context, WASMExpressionType::I32, WASMExpressionType::F32, WASMTypeConversion::ConvertSigned); + case WASMOpExpressionF32::FromU32: + return parseConvertType(context, WASMExpressionType::I32, WASMExpressionType::F32, WASMTypeConversion::ConvertUnsigned); + case WASMOpExpressionF32::FromF64: + return parseConvertType(context, WASMExpressionType::F64, WASMExpressionType::F32, WASMTypeConversion::Demote); + case WASMOpExpressionF32::Negate: + case WASMOpExpressionF32::Abs: + case WASMOpExpressionF32::Ceil: + case WASMOpExpressionF32::Floor: + case WASMOpExpressionF32::Sqrt: + return parseUnaryExpressionF32(context, op); + case WASMOpExpressionF32::Add: + case WASMOpExpressionF32::Sub: + case WASMOpExpressionF32::Mul: + case WASMOpExpressionF32::Div: + return parseBinaryExpressionF32(context, op); + default: + ASSERT_NOT_REACHED(); + } + } else { + switch (opWithImmediate) { + case WASMOpExpressionF32WithImmediate::ConstantPoolIndex: + return parseConstantPoolIndexExpressionF32(context, immediate); + case WASMOpExpressionF32WithImmediate::GetLocal: + return parseGetLocalExpression(context, WASMType::F32, immediate); + default: + ASSERT_NOT_REACHED(); + } + } + return 0; +} + +template <class Context> +ContextExpression WASMFunctionParser::parseConstantPoolIndexExpressionF32(Context& context, uint32_t constantIndex) +{ + FAIL_IF_FALSE(constantIndex < m_module->f32Constants().size(), "The constant pool index is incorrect."); + return context.buildImmediateF32(m_module->f32Constants()[constantIndex]); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseConstantPoolIndexExpressionF32(Context& context) +{ + uint32_t constantIndex; + READ_COMPACT_UINT32_OR_FAIL(constantIndex, "Cannot read the constant pool index."); + return parseConstantPoolIndexExpressionF32(context, constantIndex); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseImmediateExpressionF32(Context& context) +{ + float immediate; + READ_FLOAT_OR_FAIL(immediate, "Cannot read the immediate."); + return context.buildImmediateF32(immediate); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseUnaryExpressionF32(Context& context, WASMOpExpressionF32 op) +{ + ContextExpression expression = parseExpressionF32(context); + PROPAGATE_ERROR(); + return context.buildUnaryF32(expression, op); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseBinaryExpressionF32(Context& context, WASMOpExpressionF32 op) +{ + ContextExpression left = parseExpressionF32(context); + PROPAGATE_ERROR(); + ContextExpression right = parseExpressionF32(context); + PROPAGATE_ERROR(); + return context.buildBinaryF32(left, right, op); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseExpressionF64(Context& context) +{ + bool hasImmediate; + WASMOpExpressionF64 op; + WASMOpExpressionF64WithImmediate opWithImmediate; + uint8_t immediate; + READ_OP_EXPRESSION_F64_OR_FAIL(hasImmediate, op, opWithImmediate, immediate, "Cannot read the float64 expression opcode."); + if (!hasImmediate) { + switch (op) { + case WASMOpExpressionF64::ConstantPoolIndex: + return parseConstantPoolIndexExpressionF64(context); + case WASMOpExpressionF64::Immediate: + return parseImmediateExpressionF64(context); + case WASMOpExpressionF64::GetLocal: + return parseGetLocalExpression(context, WASMType::F64); + case WASMOpExpressionF64::GetGlobal: + return parseGetGlobalExpression(context, WASMType::F64); + case WASMOpExpressionF64::SetLocal: + return parseSetLocal(context, WASMOpKind::Expression, WASMExpressionType::F64); + case WASMOpExpressionF64::SetGlobal: + return parseSetGlobal(context, WASMOpKind::Expression, WASMExpressionType::F64); + case WASMOpExpressionF64::Load: + return parseLoad(context, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::NoOffset); + case WASMOpExpressionF64::LoadWithOffset: + return parseLoad(context, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::WithOffset); + case WASMOpExpressionF64::Store: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::NoOffset); + case WASMOpExpressionF64::StoreWithOffset: + return parseStore(context, WASMOpKind::Expression, WASMExpressionType::F64, WASMMemoryType::F64, MemoryAccessOffsetMode::WithOffset); + case WASMOpExpressionF64::CallInternal: + return parseCallInternal(context, WASMOpKind::Expression, WASMExpressionType::F64); + case WASMOpExpressionF64::CallImport: + return parseCallImport(context, WASMOpKind::Expression, WASMExpressionType::F64); + case WASMOpExpressionF64::CallIndirect: + return parseCallIndirect(context, WASMOpKind::Expression, WASMExpressionType::F64); + case WASMOpExpressionF64::Conditional: + return parseConditional(context, WASMExpressionType::F64); + case WASMOpExpressionF64::Comma: + return parseComma(context, WASMExpressionType::F64); + case WASMOpExpressionF64::FromS32: + return parseConvertType(context, WASMExpressionType::I32, WASMExpressionType::F64, WASMTypeConversion::ConvertSigned); + case WASMOpExpressionF64::FromU32: + return parseConvertType(context, WASMExpressionType::I32, WASMExpressionType::F64, WASMTypeConversion::ConvertUnsigned); + case WASMOpExpressionF64::FromF32: + return parseConvertType(context, WASMExpressionType::F32, WASMExpressionType::F64, WASMTypeConversion::Promote); + case WASMOpExpressionF64::Negate: + case WASMOpExpressionF64::Abs: + case WASMOpExpressionF64::Ceil: + case WASMOpExpressionF64::Floor: + case WASMOpExpressionF64::Sqrt: + case WASMOpExpressionF64::Cos: + case WASMOpExpressionF64::Sin: + case WASMOpExpressionF64::Tan: + case WASMOpExpressionF64::ACos: + case WASMOpExpressionF64::ASin: + case WASMOpExpressionF64::ATan: + case WASMOpExpressionF64::Exp: + case WASMOpExpressionF64::Ln: + return parseUnaryExpressionF64(context, op); + case WASMOpExpressionF64::Add: + case WASMOpExpressionF64::Sub: + case WASMOpExpressionF64::Mul: + case WASMOpExpressionF64::Div: + case WASMOpExpressionF64::Mod: + case WASMOpExpressionF64::ATan2: + case WASMOpExpressionF64::Pow: + return parseBinaryExpressionF64(context, op); + case WASMOpExpressionF64::Min: + case WASMOpExpressionF64::Max: + return parseMinOrMaxExpressionF64(context, op); + default: + ASSERT_NOT_REACHED(); + } + } else { + switch (opWithImmediate) { + case WASMOpExpressionF64WithImmediate::ConstantPoolIndex: + return parseConstantPoolIndexExpressionF64(context, immediate); + case WASMOpExpressionF64WithImmediate::GetLocal: + return parseGetLocalExpression(context, WASMType::F64, immediate); + default: + ASSERT_NOT_REACHED(); + } + } + return 0; +} + +template <class Context> +ContextExpression WASMFunctionParser::parseConstantPoolIndexExpressionF64(Context& context, uint32_t constantIndex) +{ + FAIL_IF_FALSE(constantIndex < m_module->f64Constants().size(), "The constant index is incorrect."); + return context.buildImmediateF64(m_module->f64Constants()[constantIndex]); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseConstantPoolIndexExpressionF64(Context& context) +{ + uint32_t constantIndex; + READ_COMPACT_UINT32_OR_FAIL(constantIndex, "Cannot read the constant index."); + return parseConstantPoolIndexExpressionF64(context, constantIndex); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseImmediateExpressionF64(Context& context) +{ + double immediate; + READ_DOUBLE_OR_FAIL(immediate, "Cannot read the immediate."); + return context.buildImmediateF64(immediate); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseUnaryExpressionF64(Context& context, WASMOpExpressionF64 op) +{ + ContextExpression expression = parseExpressionF64(context); + PROPAGATE_ERROR(); + return context.buildUnaryF64(expression, op); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseBinaryExpressionF64(Context& context, WASMOpExpressionF64 op) +{ + ContextExpression left = parseExpressionF64(context); + PROPAGATE_ERROR(); + ContextExpression right = parseExpressionF64(context); + PROPAGATE_ERROR(); + return context.buildBinaryF64(left, right, op); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseMinOrMaxExpressionF64(Context& context, WASMOpExpressionF64 op) +{ + uint32_t numberOfArguments; + READ_COMPACT_UINT32_OR_FAIL(numberOfArguments, "Cannot read the number of arguments to min/max."); + FAIL_IF_FALSE(numberOfArguments >= 2, "Min/max must be passed at least 2 arguments."); + ContextExpression current = parseExpressionF64(context); + PROPAGATE_ERROR(); + for (uint32_t i = 1; i < numberOfArguments; ++i) { + ContextExpression expression = parseExpressionF64(context); + PROPAGATE_ERROR(); + current = context.buildMinOrMaxF64(current, expression, op); + } + return current; +} + +template <class Context> +ContextExpression WASMFunctionParser::parseExpressionVoid(Context& context) +{ + WASMOpExpressionVoid op; + READ_OP_EXPRESSION_VOID_OR_FAIL(op, "Cannot read the void expression opcode."); + switch (op) { + case WASMOpExpressionVoid::CallInternal: + return parseCallInternal(context, WASMOpKind::Expression, WASMExpressionType::Void); + case WASMOpExpressionVoid::CallIndirect: + return parseCallIndirect(context, WASMOpKind::Expression, WASMExpressionType::Void); + case WASMOpExpressionVoid::CallImport: + return parseCallImport(context, WASMOpKind::Expression, WASMExpressionType::Void); + default: + RELEASE_ASSERT_NOT_REACHED(); + } +} + +template <class Context> +ContextExpression WASMFunctionParser::parseGetLocalExpression(Context& context, WASMType type, uint32_t localIndex) +{ + FAIL_IF_FALSE(localIndex < m_localTypes.size(), "The local index is incorrect."); + FAIL_IF_FALSE(m_localTypes[localIndex] == type, "Expected a local of type " + nameOfType(type) + '.'); + return context.buildGetLocal(localIndex, type); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseGetLocalExpression(Context& context, WASMType type) +{ + uint32_t localIndex; + READ_COMPACT_UINT32_OR_FAIL(localIndex, "Cannot read the local index."); + return parseGetLocalExpression(context, type, localIndex); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseGetGlobalExpression(Context& context, WASMType type) +{ + uint32_t globalIndex; + READ_COMPACT_UINT32_OR_FAIL(globalIndex, "Cannot read the global index."); + FAIL_IF_FALSE(globalIndex < m_module->globalVariableTypes().size(), "The global index is incorrect."); + FAIL_IF_FALSE(m_module->globalVariableTypes()[globalIndex] == type, "Expected a global of type " + nameOfType(type) + '.'); + return context.buildGetGlobal(globalIndex, type); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseSetLocal(Context& context, WASMOpKind opKind, WASMExpressionType expressionType, uint32_t localIndex) +{ + FAIL_IF_FALSE(localIndex < m_localTypes.size(), "The local variable index is incorrect."); + WASMType type = m_localTypes[localIndex]; + if (opKind == WASMOpKind::Expression) + FAIL_IF_FALSE(expressionType == WASMExpressionType(type), "The type doesn't match."); + ContextExpression expression = parseExpression(context, WASMExpressionType(type)); + PROPAGATE_ERROR(); + return context.buildSetLocal(opKind, localIndex, expression, type); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseSetLocal(Context& context, WASMOpKind opKind, WASMExpressionType expressionType) +{ + uint32_t localIndex; + READ_COMPACT_UINT32_OR_FAIL(localIndex, "Cannot read the local index."); + return parseSetLocal(context, opKind, expressionType, localIndex); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseSetGlobal(Context& context, WASMOpKind opKind, WASMExpressionType expressionType, uint32_t globalIndex) +{ + FAIL_IF_FALSE(globalIndex < m_module->globalVariableTypes().size(), "The global index is incorrect."); + WASMType type = m_module->globalVariableTypes()[globalIndex]; + if (opKind == WASMOpKind::Expression) + FAIL_IF_FALSE(expressionType == WASMExpressionType(type), "The type doesn't match."); + ContextExpression expression = parseExpression(context, WASMExpressionType(type)); + PROPAGATE_ERROR(); + return context.buildSetGlobal(opKind, globalIndex, expression, type); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseSetGlobal(Context& context, WASMOpKind opKind, WASMExpressionType expressionType) +{ + uint32_t globalIndex; + READ_COMPACT_UINT32_OR_FAIL(globalIndex, "Cannot read the global index."); + return parseSetGlobal(context, opKind, expressionType, globalIndex); +} + +template <class Context> +ContextMemoryAddress WASMFunctionParser::parseMemoryAddress(Context& context, MemoryAccessOffsetMode offsetMode) +{ + uint32_t offset = 0; + if (offsetMode == MemoryAccessOffsetMode::WithOffset) + READ_COMPACT_UINT32_OR_FAIL(offset, "Cannot read the address offset."); + ContextExpression index = parseExpressionI32(context); + PROPAGATE_ERROR(); + return ContextMemoryAddress(index, offset); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseLoad(Context& context, WASMExpressionType expressionType, WASMMemoryType memoryType, MemoryAccessOffsetMode offsetMode, MemoryAccessConversion conversion) +{ + FAIL_IF_FALSE(m_module->arrayBuffer(), "An ArrayBuffer is not provided."); + const ContextMemoryAddress& memoryAddress = parseMemoryAddress(context, offsetMode); + PROPAGATE_ERROR(); + return context.buildLoad(memoryAddress, expressionType, memoryType, conversion); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseStore(Context& context, WASMOpKind opKind, WASMExpressionType expressionType, WASMMemoryType memoryType, MemoryAccessOffsetMode offsetMode) +{ + FAIL_IF_FALSE(m_module->arrayBuffer(), "An ArrayBuffer is not provided."); + const ContextMemoryAddress& memoryAddress = parseMemoryAddress(context, offsetMode); + PROPAGATE_ERROR(); + + ContextExpression value = parseExpression(context, expressionType); + PROPAGATE_ERROR(); + return context.buildStore(opKind, memoryAddress, expressionType, memoryType, value); +} + +template <class Context> +ContextExpressionList WASMFunctionParser::parseCallArguments(Context& context, const Vector<WASMType>& arguments) +{ + ContextExpressionList argumentList; + for (size_t i = 0; i < arguments.size(); ++i) { + ContextExpression expression = parseExpression(context, WASMExpressionType(arguments[i])); + PROPAGATE_ERROR(); + context.appendExpressionList(argumentList, expression); + } + return argumentList; +} + +template <class Context> +ContextExpression WASMFunctionParser::parseCallInternal(Context& context, WASMOpKind opKind, WASMExpressionType returnType) +{ + uint32_t functionIndex; + READ_COMPACT_UINT32_OR_FAIL(functionIndex, "Cannot read the function index."); + FAIL_IF_FALSE(functionIndex < m_module->functionDeclarations().size(), "The function index is incorrect."); + const WASMSignature& signature = m_module->signatures()[m_module->functionDeclarations()[functionIndex].signatureIndex]; + if (opKind == WASMOpKind::Expression) + FAIL_IF_FALSE(signature.returnType == returnType, "Wrong return type."); + + ContextExpressionList argumentList = parseCallArguments(context, signature.arguments); + PROPAGATE_ERROR(); + return context.buildCallInternal(functionIndex, argumentList, signature, returnType); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseCallIndirect(Context& context, WASMOpKind opKind, WASMExpressionType returnType) +{ + uint32_t functionPointerTableIndex; + READ_COMPACT_UINT32_OR_FAIL(functionPointerTableIndex, "Cannot read the function pointer table index."); + FAIL_IF_FALSE(functionPointerTableIndex < m_module->functionPointerTables().size(), "The function pointer table index is incorrect."); + const WASMFunctionPointerTable& functionPointerTable = m_module->functionPointerTables()[functionPointerTableIndex]; + const WASMSignature& signature = m_module->signatures()[functionPointerTable.signatureIndex]; + if (opKind == WASMOpKind::Expression) + FAIL_IF_FALSE(signature.returnType == returnType, "Wrong return type."); + + ContextExpression index = parseExpressionI32(context); + PROPAGATE_ERROR(); + + ContextExpressionList argumentList = parseCallArguments(context, signature.arguments); + PROPAGATE_ERROR(); + return context.buildCallIndirect(functionPointerTableIndex, index, argumentList, signature, returnType); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseCallImport(Context& context, WASMOpKind opKind, WASMExpressionType returnType) +{ + uint32_t functionImportSignatureIndex; + READ_COMPACT_UINT32_OR_FAIL(functionImportSignatureIndex, "Cannot read the function import signature index."); + FAIL_IF_FALSE(functionImportSignatureIndex < m_module->functionImportSignatures().size(), "The function import signature index is incorrect."); + const WASMFunctionImportSignature& functionImportSignature = m_module->functionImportSignatures()[functionImportSignatureIndex]; + const WASMSignature& signature = m_module->signatures()[functionImportSignature.signatureIndex]; + if (opKind == WASMOpKind::Expression) + FAIL_IF_FALSE(signature.returnType == returnType, "Wrong return type."); + + ContextExpressionList argumentList = parseCallArguments(context, signature.arguments); + PROPAGATE_ERROR(); + return context.buildCallImport(functionImportSignature.functionImportIndex, argumentList, signature, returnType); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseConditional(Context& context, WASMExpressionType expressionType) +{ + ContextJumpTarget elseTarget; + ContextJumpTarget end; + + ContextExpression condition = parseExpressionI32(context); + PROPAGATE_ERROR(); + + context.jumpToTargetIf(Context::JumpCondition::Zero, condition, elseTarget); + + parseExpression(context, expressionType); + PROPAGATE_ERROR(); + + context.jumpToTarget(end); + context.linkTarget(elseTarget); + + // We use discard() here to decrement the stack top in the baseline JIT. + context.discard(UNUSED); + parseExpression(context, expressionType); + PROPAGATE_ERROR(); + + context.linkTarget(end); + return UNUSED; +} + +template <class Context> +ContextExpression WASMFunctionParser::parseComma(Context& context, WASMExpressionType expressionType) +{ + WASMExpressionType leftExpressionType; + READ_EXPRESSION_TYPE_OR_FAIL(leftExpressionType, "Cannot read the expression type."); + ContextExpression leftExpression = parseExpression(context, leftExpressionType); + PROPAGATE_ERROR(); + if (leftExpressionType != WASMExpressionType::Void) + context.discard(leftExpression); + return parseExpression(context, expressionType); +} + +template <class Context> +ContextExpression WASMFunctionParser::parseConvertType(Context& context, WASMExpressionType fromType, WASMExpressionType toType, WASMTypeConversion conversion) +{ + ContextExpression expression = parseExpression(context, fromType); + PROPAGATE_ERROR(); + + return context.buildConvertType(expression, fromType, toType, conversion); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WASMFunctionParser.h b/Source/JavaScriptCore/wasm/WASMFunctionParser.h new file mode 100644 index 000000000..7f09e3e85 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMFunctionParser.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WASMFunctionParser_h +#define WASMFunctionParser_h + +#if ENABLE(WEBASSEMBLY) + +#include "SourceCode.h" +#include "WASMReader.h" + +#define ContextExpression typename Context::Expression +#define ContextStatement typename Context::Statement +#define ContextExpressionList typename Context::ExpressionList +#define ContextMemoryAddress typename Context::MemoryAddress +#define ContextJumpTarget typename Context::JumpTarget + +namespace JSC { + +class CodeBlock; +class JSWASMModule; +class VM; + +class WASMFunctionParser { +public: + static bool checkSyntax(JSWASMModule*, const SourceCode&, size_t functionIndex, unsigned startOffsetInSource, unsigned& endOffsetInSource, unsigned& stackHeight, String& errorMessage); + static void compile(VM&, CodeBlock*, JSWASMModule*, const SourceCode&, size_t functionIndex); + +private: + WASMFunctionParser(JSWASMModule* module, const SourceCode& source, size_t functionIndex) + : m_module(module) + , m_reader(static_cast<WebAssemblySourceProvider*>(source.provider())->data()) + , m_functionIndex(functionIndex) + , m_breakScopeDepth(0) + , m_continueScopeDepth(0) + , m_labelDepth(0) + { + } + + template <class Context> bool parseFunction(Context&); + bool parseLocalVariables(); + + template <class Context> ContextStatement parseStatement(Context&); + template <class Context> ContextStatement parseReturnStatement(Context&); + template <class Context> ContextStatement parseBlockStatement(Context&); + template <class Context> ContextStatement parseIfStatement(Context&); + template <class Context> ContextStatement parseIfElseStatement(Context&); + template <class Context> ContextStatement parseWhileStatement(Context&); + template <class Context> ContextStatement parseDoStatement(Context&); + template <class Context> ContextStatement parseLabelStatement(Context&); + template <class Context> ContextStatement parseBreakStatement(Context&); + template <class Context> ContextStatement parseBreakLabelStatement(Context&); + template <class Context> ContextStatement parseContinueStatement(Context&); + template <class Context> ContextStatement parseContinueLabelStatement(Context&); + template <class Context> ContextStatement parseSwitchStatement(Context&); + + template <class Context> ContextExpression parseExpression(Context&, WASMExpressionType); + + template <class Context> ContextExpression parseExpressionI32(Context&); + template <class Context> ContextExpression parseConstantPoolIndexExpressionI32(Context&, uint32_t constantPoolIndex); + template <class Context> ContextExpression parseConstantPoolIndexExpressionI32(Context&); + template <class Context> ContextExpression parseImmediateExpressionI32(Context&, uint32_t immediate); + template <class Context> ContextExpression parseImmediateExpressionI32(Context&); + template <class Context> ContextExpression parseUnaryExpressionI32(Context&, WASMOpExpressionI32); + template <class Context> ContextExpression parseBinaryExpressionI32(Context&, WASMOpExpressionI32); + template <class Context> ContextExpression parseRelationalI32ExpressionI32(Context&, WASMOpExpressionI32); + template <class Context> ContextExpression parseRelationalF32ExpressionI32(Context&, WASMOpExpressionI32); + template <class Context> ContextExpression parseRelationalF64ExpressionI32(Context&, WASMOpExpressionI32); + template <class Context> ContextExpression parseMinOrMaxExpressionI32(Context&, WASMOpExpressionI32); + + template <class Context> ContextExpression parseExpressionF32(Context&); + template <class Context> ContextExpression parseConstantPoolIndexExpressionF32(Context&, uint32_t constantIndex); + template <class Context> ContextExpression parseConstantPoolIndexExpressionF32(Context&); + template <class Context> ContextExpression parseImmediateExpressionF32(Context&); + template <class Context> ContextExpression parseUnaryExpressionF32(Context&, WASMOpExpressionF32); + template <class Context> ContextExpression parseBinaryExpressionF32(Context&, WASMOpExpressionF32); + + template <class Context> ContextExpression parseExpressionF64(Context&); + template <class Context> ContextExpression parseConstantPoolIndexExpressionF64(Context&, uint32_t constantIndex); + template <class Context> ContextExpression parseConstantPoolIndexExpressionF64(Context&); + template <class Context> ContextExpression parseImmediateExpressionF64(Context&); + template <class Context> ContextExpression parseUnaryExpressionF64(Context&, WASMOpExpressionF64); + template <class Context> ContextExpression parseBinaryExpressionF64(Context&, WASMOpExpressionF64); + template <class Context> ContextExpression parseMinOrMaxExpressionF64(Context&, WASMOpExpressionF64); + + template <class Context> ContextExpression parseExpressionVoid(Context&); + + template <class Context> ContextExpression parseGetLocalExpression(Context&, WASMType, uint32_t localIndex); + template <class Context> ContextExpression parseGetLocalExpression(Context&, WASMType); + template <class Context> ContextExpression parseGetGlobalExpression(Context&, WASMType); + template <class Context> ContextExpression parseSetLocal(Context&, WASMOpKind, WASMExpressionType, uint32_t localIndex); + template <class Context> ContextExpression parseSetLocal(Context&, WASMOpKind, WASMExpressionType); + template <class Context> ContextExpression parseSetGlobal(Context&, WASMOpKind, WASMExpressionType, uint32_t globalIndex); + template <class Context> ContextExpression parseSetGlobal(Context&, WASMOpKind, WASMExpressionType); + template <class Context> ContextMemoryAddress parseMemoryAddress(Context&, MemoryAccessOffsetMode); + template <class Context> ContextExpression parseLoad(Context&, WASMExpressionType, WASMMemoryType, MemoryAccessOffsetMode, MemoryAccessConversion = MemoryAccessConversion::NoConversion); + template <class Context> ContextExpression parseStore(Context&, WASMOpKind, WASMExpressionType, WASMMemoryType, MemoryAccessOffsetMode); + template <class Context> ContextExpressionList parseCallArguments(Context&, const Vector<WASMType>& arguments); + template <class Context> ContextExpression parseCallInternal(Context&, WASMOpKind, WASMExpressionType returnType); + template <class Context> ContextExpression parseCallIndirect(Context&, WASMOpKind, WASMExpressionType returnType); + template <class Context> ContextExpression parseCallImport(Context&, WASMOpKind, WASMExpressionType returnType); + template <class Context> ContextExpression parseConditional(Context&, WASMExpressionType); + template <class Context> ContextExpression parseComma(Context&, WASMExpressionType); + template <class Context> ContextExpression parseConvertType(Context&, WASMExpressionType fromType, WASMExpressionType toType, WASMTypeConversion); + + JSWASMModule* m_module; + WASMReader m_reader; + size_t m_functionIndex; + String m_errorMessage; + + WASMExpressionType m_returnType; + Vector<WASMType> m_localTypes; + uint32_t m_numberOfI32LocalVariables; + uint32_t m_numberOfF32LocalVariables; + uint32_t m_numberOfF64LocalVariables; + + unsigned m_breakScopeDepth; + unsigned m_continueScopeDepth; + unsigned m_labelDepth; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // WASMFunctionParser_h diff --git a/Source/JavaScriptCore/wasm/WASMFunctionSyntaxChecker.h b/Source/JavaScriptCore/wasm/WASMFunctionSyntaxChecker.h new file mode 100644 index 000000000..edfe42808 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMFunctionSyntaxChecker.h @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WASMFunctionSyntaxChecker_h +#define WASMFunctionSyntaxChecker_h + +#if ENABLE(WEBASSEMBLY) + +#define UNUSED 0 + +namespace JSC { + +class WASMFunctionSyntaxChecker { +public: + typedef int Expression; + typedef int Statement; + typedef int ExpressionList; + struct MemoryAddress { + MemoryAddress(void*) { } + MemoryAddress(int, uint32_t) { } + }; + typedef int JumpTarget; + enum class JumpCondition { Zero, NonZero }; + + void startFunction(const Vector<WASMType>& arguments, uint32_t numberOfI32LocalVariables, uint32_t numberOfF32LocalVariables, uint32_t numberOfF64LocalVariables) + { + m_numberOfLocals = arguments.size() + numberOfI32LocalVariables + numberOfF32LocalVariables + numberOfF64LocalVariables; + } + + void endFunction() + { + ASSERT(!m_tempStackTop); + } + + int buildSetLocal(WASMOpKind opKind, uint32_t, int, WASMType) + { + if (opKind == WASMOpKind::Statement) + m_tempStackTop--; + return UNUSED; + } + + int buildSetGlobal(WASMOpKind opKind, uint32_t, int, WASMType) + { + if (opKind == WASMOpKind::Statement) + m_tempStackTop--; + return UNUSED; + } + + void buildReturn(int, WASMExpressionType returnType) + { + if (returnType != WASMExpressionType::Void) + m_tempStackTop--; + } + + int buildImmediateI32(uint32_t) + { + m_tempStackTop++; + updateTempStackHeight(); + return UNUSED; + } + + int buildImmediateF32(float) + { + m_tempStackTop++; + updateTempStackHeight(); + return UNUSED; + } + + int buildImmediateF64(double) + { + m_tempStackTop++; + updateTempStackHeight(); + return UNUSED; + } + + int buildGetLocal(uint32_t, WASMType) + { + m_tempStackTop++; + updateTempStackHeight(); + return UNUSED; + } + + int buildGetGlobal(uint32_t, WASMType) + { + m_tempStackTop++; + updateTempStackHeight(); + return UNUSED; + } + + int buildConvertType(int, WASMExpressionType, WASMExpressionType, WASMTypeConversion) + { + return UNUSED; + } + + int buildLoad(const MemoryAddress&, WASMExpressionType, WASMMemoryType, MemoryAccessConversion) + { + return UNUSED; + } + + int buildStore(WASMOpKind opKind, const MemoryAddress&, WASMExpressionType, WASMMemoryType, int) + { + m_tempStackTop -= 2; + if (opKind == WASMOpKind::Expression) + m_tempStackTop++; + return UNUSED; + } + + int buildUnaryI32(int, WASMOpExpressionI32) + { + return UNUSED; + } + + int buildUnaryF32(int, WASMOpExpressionF32) + { + return UNUSED; + } + + int buildUnaryF64(int, WASMOpExpressionF64) + { + return UNUSED; + } + + int buildBinaryI32(int, int, WASMOpExpressionI32) + { + m_tempStackTop--; + return UNUSED; + } + + int buildBinaryF32(int, int, WASMOpExpressionF32) + { + m_tempStackTop--; + return UNUSED; + } + + int buildBinaryF64(int, int, WASMOpExpressionF64) + { + m_tempStackTop--; + return UNUSED; + } + + int buildRelationalI32(int, int, WASMOpExpressionI32) + { + m_tempStackTop--; + return UNUSED; + } + + int buildRelationalF32(int, int, WASMOpExpressionI32) + { + m_tempStackTop--; + return UNUSED; + } + + int buildRelationalF64(int, int, WASMOpExpressionI32) + { + m_tempStackTop--; + return UNUSED; + } + + int buildMinOrMaxI32(int, int, WASMOpExpressionI32) + { + m_tempStackTop--; + return UNUSED; + } + + int buildMinOrMaxF64(int, int, WASMOpExpressionF64) + { + m_tempStackTop--; + return UNUSED; + } + + int buildCallInternal(uint32_t, int, const WASMSignature& signature, WASMExpressionType returnType) + { + size_t argumentCount = signature.arguments.size(); + updateTempStackHeightForCall(argumentCount); + m_tempStackTop -= argumentCount; + if (returnType != WASMExpressionType::Void) { + m_tempStackTop++; + updateTempStackHeight(); + } + return UNUSED; + } + + int buildCallImport(uint32_t, int, const WASMSignature& signature, WASMExpressionType returnType) + { + size_t argumentCount = signature.arguments.size(); + updateTempStackHeightForCall(argumentCount); + m_tempStackTop -= argumentCount; + if (returnType != WASMExpressionType::Void) { + m_tempStackTop++; + updateTempStackHeight(); + } + return UNUSED; + } + + int buildCallIndirect(uint32_t, int, int, const WASMSignature& signature, WASMExpressionType returnType) + { + size_t argumentCount = signature.arguments.size(); + updateTempStackHeightForCall(argumentCount); + m_tempStackTop -= argumentCount + 1; + if (returnType != WASMExpressionType::Void) + m_tempStackTop++; + return UNUSED; + } + + void appendExpressionList(int&, int) { } + + void discard(int) + { + m_tempStackTop--; + } + + void linkTarget(const int&) { } + void jumpToTarget(const int&) { } + void jumpToTargetIf(JumpCondition, int, const int&) + { + m_tempStackTop--; + } + + void startLoop() { } + void endLoop() { } + void startSwitch() { } + void endSwitch() { } + void startLabel() { } + void endLabel() { } + + int breakTarget() { return UNUSED; } + int continueTarget() { return UNUSED; } + int breakLabelTarget(uint32_t) { return UNUSED; } + int continueLabelTarget(uint32_t) { return UNUSED; } + + void buildSwitch(int, const Vector<int64_t>&, const Vector<int>&, const int&) + { + m_tempStackTop--; + } + + unsigned stackHeight() + { + return m_numberOfLocals + m_tempStackHeight; + } + +private: + void updateTempStackHeight() + { + if (m_tempStackTop > m_tempStackHeight) + m_tempStackHeight = m_tempStackTop; + } + + void updateTempStackHeightForCall(size_t argumentCount) + { + // Boxed arguments + this argument + call frame header + maximum padding. + m_tempStackTop += argumentCount + 1 + JSStack::CallFrameHeaderSize + 1; + updateTempStackHeight(); + m_tempStackTop -= argumentCount + 1 + JSStack::CallFrameHeaderSize + 1; + } + + unsigned m_numberOfLocals; + unsigned m_tempStackTop { 0 }; + unsigned m_tempStackHeight { 0 }; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // WASMFunctionSyntaxChecker_h diff --git a/Source/JavaScriptCore/wasm/WASMModuleParser.cpp b/Source/JavaScriptCore/wasm/WASMModuleParser.cpp new file mode 100644 index 000000000..8ce35f483 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMModuleParser.cpp @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WASMModuleParser.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSArrayBuffer.h" +#include "JSCInlines.h" +#include "JSWASMModule.h" +#include "StrongInlines.h" +#include "WASMConstants.h" +#include "WASMFunctionParser.h" +#include <wtf/MathExtras.h> + +#define FAIL_WITH_MESSAGE(errorMessage) do { m_errorMessage = errorMessage; return; } while (0) +#define READ_UINT32_OR_FAIL(result, errorMessage) do { if (!m_reader.readUInt32(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_FLOAT_OR_FAIL(result, errorMessage) do { if (!m_reader.readFloat(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_DOUBLE_OR_FAIL(result, errorMessage) do { if (!m_reader.readDouble(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_COMPACT_UINT32_OR_FAIL(result, errorMessage) do { if (!m_reader.readCompactUInt32(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_STRING_OR_FAIL(result, errorMessage) do { if (!m_reader.readString(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_TYPE_OR_FAIL(result, errorMessage) do { if (!m_reader.readType(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_EXPRESSION_TYPE_OR_FAIL(result, errorMessage) do { if (!m_reader.readExpressionType(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define READ_EXPORT_FORMAT_OR_FAIL(result, errorMessage) do { if (!m_reader.readExportFormat(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define FAIL_IF_FALSE(condition, errorMessage) do { if (!(condition)) FAIL_WITH_MESSAGE(errorMessage); } while (0) +#define PROPAGATE_ERROR() do { if (!m_errorMessage.isNull()) return; } while (0) + +namespace JSC { + +WASMModuleParser::WASMModuleParser(VM& vm, JSGlobalObject* globalObject, const SourceCode& source, JSObject* imports, JSArrayBuffer* arrayBuffer) + : m_vm(vm) + , m_globalObject(vm, globalObject) + , m_source(source) + , m_imports(vm, imports) + , m_reader(static_cast<WebAssemblySourceProvider*>(source.provider())->data()) + , m_module(vm, JSWASMModule::create(vm, globalObject->wasmModuleStructure(), arrayBuffer)) +{ +} + +JSWASMModule* WASMModuleParser::parse(ExecState* exec, String& errorMessage) +{ + parseModule(exec); + if (!m_errorMessage.isNull()) { + errorMessage = m_errorMessage; + return nullptr; + } + return m_module.get(); +} + +void WASMModuleParser::parseModule(ExecState* exec) +{ + uint32_t magicNumber; + READ_UINT32_OR_FAIL(magicNumber, "Cannot read the magic number."); + FAIL_IF_FALSE(magicNumber == wasmMagicNumber, "The magic number is incorrect."); + + uint32_t outputSizeInASMJS; + READ_UINT32_OR_FAIL(outputSizeInASMJS, "Cannot read the output size in asm.js format."); + + parseConstantPoolSection(); + PROPAGATE_ERROR(); + parseSignatureSection(); + PROPAGATE_ERROR(); + parseFunctionImportSection(exec); + PROPAGATE_ERROR(); + parseGlobalSection(exec); + PROPAGATE_ERROR(); + parseFunctionDeclarationSection(); + PROPAGATE_ERROR(); + parseFunctionPointerTableSection(); + PROPAGATE_ERROR(); + parseFunctionDefinitionSection(); + PROPAGATE_ERROR(); + parseExportSection(); + PROPAGATE_ERROR(); + + FAIL_IF_FALSE(!m_module->arrayBuffer() || m_module->arrayBuffer()->impl()->byteLength() < (1u << 31), "The ArrayBuffer's length must be less than 2^31."); +} + +void WASMModuleParser::parseConstantPoolSection() +{ + uint32_t numberOfI32Constants; + uint32_t numberOfF32Constants; + uint32_t numberOfF64Constants; + READ_COMPACT_UINT32_OR_FAIL(numberOfI32Constants, "Cannot read the number of int32 constants."); + READ_COMPACT_UINT32_OR_FAIL(numberOfF32Constants, "Cannot read the number of float32 constants."); + READ_COMPACT_UINT32_OR_FAIL(numberOfF64Constants, "Cannot read the number of float64 constants."); + m_module->i32Constants().reserveInitialCapacity(numberOfI32Constants); + m_module->f32Constants().reserveInitialCapacity(numberOfF32Constants); + m_module->f64Constants().reserveInitialCapacity(numberOfF64Constants); + + for (uint32_t i = 0; i < numberOfI32Constants; ++i) { + uint32_t constant; + READ_COMPACT_UINT32_OR_FAIL(constant, "Cannot read an int32 constant."); + m_module->i32Constants().uncheckedAppend(constant); + } + for (uint32_t i = 0; i < numberOfF32Constants; ++i) { + float constant; + READ_FLOAT_OR_FAIL(constant, "Cannot read a float32 constant."); + m_module->f32Constants().uncheckedAppend(constant); + } + for (uint32_t i = 0; i < numberOfF64Constants; ++i) { + double constant; + READ_DOUBLE_OR_FAIL(constant, "Cannot read a float64 constant."); + m_module->f64Constants().uncheckedAppend(constant); + } +} + +void WASMModuleParser::parseSignatureSection() +{ + uint32_t numberOfSignatures; + READ_COMPACT_UINT32_OR_FAIL(numberOfSignatures, "Cannot read the number of signatures."); + m_module->signatures().reserveInitialCapacity(numberOfSignatures); + for (uint32_t signatureIndex = 0; signatureIndex < numberOfSignatures; ++signatureIndex) { + WASMSignature signature; + READ_EXPRESSION_TYPE_OR_FAIL(signature.returnType, "Cannot read the return type."); + uint32_t argumentCount; + READ_COMPACT_UINT32_OR_FAIL(argumentCount, "Cannot read the number of arguments."); + signature.arguments.reserveInitialCapacity(argumentCount); + for (uint32_t argumentIndex = 0; argumentIndex < argumentCount; ++argumentIndex) { + WASMType type; + READ_TYPE_OR_FAIL(type, "Cannot read the type of an argument."); + signature.arguments.uncheckedAppend(type); + } + m_module->signatures().uncheckedAppend(signature); + } +} + +void WASMModuleParser::parseFunctionImportSection(ExecState* exec) +{ + uint32_t numberOfFunctionImports; + uint32_t numberOfFunctionImportSignatures; + READ_COMPACT_UINT32_OR_FAIL(numberOfFunctionImports, "Cannot read the number of function imports."); + READ_COMPACT_UINT32_OR_FAIL(numberOfFunctionImportSignatures, "Cannot read the number of function import signatures."); + m_module->functionImports().reserveInitialCapacity(numberOfFunctionImports); + m_module->functionImportSignatures().reserveInitialCapacity(numberOfFunctionImportSignatures); + m_module->importedFunctions().reserveInitialCapacity(numberOfFunctionImports); + + for (uint32_t functionImportIndex = 0; functionImportIndex < numberOfFunctionImports; ++functionImportIndex) { + WASMFunctionImport functionImport; + READ_STRING_OR_FAIL(functionImport.functionName, "Cannot read the function import name."); + m_module->functionImports().uncheckedAppend(functionImport); + + uint32_t numberOfSignatures; + READ_COMPACT_UINT32_OR_FAIL(numberOfSignatures, "Cannot read the number of signatures."); + FAIL_IF_FALSE(numberOfSignatures <= numberOfFunctionImportSignatures - m_module->functionImportSignatures().size(), "The number of signatures is incorrect."); + + for (uint32_t i = 0; i < numberOfSignatures; ++i) { + WASMFunctionImportSignature functionImportSignature; + READ_COMPACT_UINT32_OR_FAIL(functionImportSignature.signatureIndex, "Cannot read the signature index."); + FAIL_IF_FALSE(functionImportSignature.signatureIndex < m_module->signatures().size(), "The signature index is incorrect."); + functionImportSignature.functionImportIndex = functionImportIndex; + m_module->functionImportSignatures().uncheckedAppend(functionImportSignature); + } + + JSValue value; + getImportedValue(exec, functionImport.functionName, value); + PROPAGATE_ERROR(); + FAIL_IF_FALSE(value.isFunction(), "\"" + functionImport.functionName + "\" is not a function."); + JSFunction* function = jsCast<JSFunction*>(value.asCell()); + m_module->importedFunctions().uncheckedAppend(WriteBarrier<JSFunction>(m_vm, m_module.get(), function)); + } + FAIL_IF_FALSE(m_module->functionImportSignatures().size() == numberOfFunctionImportSignatures, "The number of function import signatures is incorrect."); +} + +void WASMModuleParser::parseGlobalSection(ExecState* exec) +{ + uint32_t numberOfInternalI32GlobalVariables; + uint32_t numberOfInternalF32GlobalVariables; + uint32_t numberOfInternalF64GlobalVariables; + uint32_t numberOfImportedI32GlobalVariables; + uint32_t numberOfImportedF32GlobalVariables; + uint32_t numberOfImportedF64GlobalVariables; + READ_COMPACT_UINT32_OR_FAIL(numberOfInternalI32GlobalVariables, "Cannot read the number of internal int32 global variables."); + READ_COMPACT_UINT32_OR_FAIL(numberOfInternalF32GlobalVariables, "Cannot read the number of internal float32 global variables."); + READ_COMPACT_UINT32_OR_FAIL(numberOfInternalF64GlobalVariables, "Cannot read the number of internal float64 global variables."); + READ_COMPACT_UINT32_OR_FAIL(numberOfImportedI32GlobalVariables, "Cannot read the number of imported int32 global variables."); + READ_COMPACT_UINT32_OR_FAIL(numberOfImportedF32GlobalVariables, "Cannot read the number of imported float32 global variables."); + READ_COMPACT_UINT32_OR_FAIL(numberOfImportedF64GlobalVariables, "Cannot read the number of imported float64 global variables."); + uint32_t numberOfGlobalVariables = numberOfInternalI32GlobalVariables + numberOfInternalF32GlobalVariables + numberOfInternalF64GlobalVariables + + numberOfImportedI32GlobalVariables + numberOfImportedF32GlobalVariables + numberOfImportedF64GlobalVariables; + + Vector<WASMType>& globalVariableTypes = m_module->globalVariableTypes(); + globalVariableTypes.reserveInitialCapacity(numberOfGlobalVariables); + Vector<JSWASMModule::GlobalVariable>& globalVariables = m_module->globalVariables(); + globalVariables.reserveInitialCapacity(numberOfGlobalVariables); + for (uint32_t i = 0; i < numberOfInternalI32GlobalVariables; ++i) { + globalVariableTypes.uncheckedAppend(WASMType::I32); + globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(0)); + } + for (uint32_t i = 0; i < numberOfInternalF32GlobalVariables; ++i) { + globalVariableTypes.uncheckedAppend(WASMType::F32); + globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(0.0f)); + } + for (uint32_t i = 0; i < numberOfInternalF64GlobalVariables; ++i) { + globalVariableTypes.uncheckedAppend(WASMType::F64); + globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(0.0)); + } + for (uint32_t i = 0; i < numberOfImportedI32GlobalVariables; ++i) { + String importName; + READ_STRING_OR_FAIL(importName, "Cannot read the import name of an int32 global variable."); + globalVariableTypes.uncheckedAppend(WASMType::I32); + JSValue value; + getImportedValue(exec, importName, value); + PROPAGATE_ERROR(); + FAIL_IF_FALSE(value.isPrimitive() && !value.isSymbol(), "\"" + importName + "\" is not a primitive or is a Symbol."); + globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(value.toInt32(exec))); + } + for (uint32_t i = 0; i < numberOfImportedF32GlobalVariables; ++i) { + String importName; + READ_STRING_OR_FAIL(importName, "Cannot read the import name of a float32 global variable."); + globalVariableTypes.uncheckedAppend(WASMType::F32); + JSValue value; + getImportedValue(exec, importName, value); + PROPAGATE_ERROR(); + FAIL_IF_FALSE(value.isPrimitive() && !value.isSymbol(), "\"" + importName + "\" is not a primitive or is a Symbol."); + globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(static_cast<float>(value.toNumber(exec)))); + } + for (uint32_t i = 0; i < numberOfImportedF64GlobalVariables; ++i) { + String importName; + READ_STRING_OR_FAIL(importName, "Cannot read the import name of a float64 global variable."); + globalVariableTypes.uncheckedAppend(WASMType::F64); + JSValue value; + getImportedValue(exec, importName, value); + PROPAGATE_ERROR(); + FAIL_IF_FALSE(value.isPrimitive() && !value.isSymbol(), "\"" + importName + "\" is not a primitive or is a Symbol."); + globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(value.toNumber(exec))); + } +} + +void WASMModuleParser::parseFunctionDeclarationSection() +{ + uint32_t numberOfFunctionDeclarations; + READ_COMPACT_UINT32_OR_FAIL(numberOfFunctionDeclarations, "Cannot read the number of function declarations."); + m_module->functionDeclarations().reserveInitialCapacity(numberOfFunctionDeclarations); + m_module->functions().reserveInitialCapacity(numberOfFunctionDeclarations); + m_module->functionStartOffsetsInSource().reserveInitialCapacity(numberOfFunctionDeclarations); + m_module->functionStackHeights().reserveInitialCapacity(numberOfFunctionDeclarations); + for (uint32_t i = 0; i < numberOfFunctionDeclarations; ++i) { + WASMFunctionDeclaration functionDeclaration; + READ_COMPACT_UINT32_OR_FAIL(functionDeclaration.signatureIndex, "Cannot read the signature index."); + FAIL_IF_FALSE(functionDeclaration.signatureIndex < m_module->signatures().size(), "The signature index is incorrect."); + m_module->functionDeclarations().uncheckedAppend(functionDeclaration); + } +} + +void WASMModuleParser::parseFunctionPointerTableSection() +{ + uint32_t numberOfFunctionPointerTables; + READ_COMPACT_UINT32_OR_FAIL(numberOfFunctionPointerTables, "Cannot read the number of function pointer tables."); + m_module->functionPointerTables().reserveInitialCapacity(numberOfFunctionPointerTables); + for (uint32_t i = 0; i < numberOfFunctionPointerTables; ++i) { + WASMFunctionPointerTable functionPointerTable; + READ_COMPACT_UINT32_OR_FAIL(functionPointerTable.signatureIndex, "Cannot read the signature index."); + FAIL_IF_FALSE(functionPointerTable.signatureIndex < m_module->signatures().size(), "The signature index is incorrect."); + uint32_t numberOfFunctions; + READ_COMPACT_UINT32_OR_FAIL(numberOfFunctions, "Cannot read the number of functions of a function pointer table."); + FAIL_IF_FALSE(hasOneBitSet(numberOfFunctions), "The number of functions must be a power of two."); + functionPointerTable.functionIndices.reserveInitialCapacity(numberOfFunctions); + functionPointerTable.functions.reserveInitialCapacity(numberOfFunctions); + for (uint32_t j = 0; j < numberOfFunctions; ++j) { + uint32_t functionIndex; + READ_COMPACT_UINT32_OR_FAIL(functionIndex, "Cannot read a function index of a function pointer table."); + FAIL_IF_FALSE(functionIndex < m_module->functionDeclarations().size(), "The function index is incorrect."); + FAIL_IF_FALSE(m_module->functionDeclarations()[functionIndex].signatureIndex == functionPointerTable.signatureIndex, "The signature of the function doesn't match that of the function pointer table."); + functionPointerTable.functionIndices.uncheckedAppend(functionIndex); + } + m_module->functionPointerTables().uncheckedAppend(functionPointerTable); + } +} + +void WASMModuleParser::parseFunctionDefinitionSection() +{ + for (size_t functionIndex = 0; functionIndex < m_module->functionDeclarations().size(); ++functionIndex) { + parseFunctionDefinition(functionIndex); + PROPAGATE_ERROR(); + } + + for (WASMFunctionPointerTable& functionPointerTable : m_module->functionPointerTables()) { + for (size_t i = 0; i < functionPointerTable.functionIndices.size(); ++i) + functionPointerTable.functions.uncheckedAppend(m_module->functions()[functionPointerTable.functionIndices[i]].get()); + } +} + +void WASMModuleParser::parseFunctionDefinition(size_t functionIndex) +{ + unsigned startOffsetInSource = m_reader.offset(); + unsigned endOffsetInSource; + unsigned stackHeight; + String errorMessage; + if (!WASMFunctionParser::checkSyntax(m_module.get(), m_source, functionIndex, startOffsetInSource, endOffsetInSource, stackHeight, errorMessage)) { + m_errorMessage = errorMessage; + return; + } + m_reader.setOffset(endOffsetInSource); + + WebAssemblyExecutable* webAssemblyExecutable = WebAssemblyExecutable::create(m_vm, m_source, m_module.get(), functionIndex); + JSFunction* function = JSFunction::create(m_vm, webAssemblyExecutable, m_globalObject.get()); + m_module->functions().uncheckedAppend(WriteBarrier<JSFunction>(m_vm, m_module.get(), function)); + m_module->functionStartOffsetsInSource().uncheckedAppend(startOffsetInSource); + m_module->functionStackHeights().uncheckedAppend(stackHeight); +} + +void WASMModuleParser::parseExportSection() +{ + WASMExportFormat exportFormat; + READ_EXPORT_FORMAT_OR_FAIL(exportFormat, "Cannot read the export format."); + switch (exportFormat) { + case WASMExportFormat::Default: { + uint32_t functionIndex; + READ_COMPACT_UINT32_OR_FAIL(functionIndex, "Cannot read the function index."); + FAIL_IF_FALSE(functionIndex < m_module->functionDeclarations().size(), "The function index is incorrect."); + // FIXME: Export the function. + break; + } + case WASMExportFormat::Record: { + uint32_t numberOfExports; + READ_COMPACT_UINT32_OR_FAIL(numberOfExports, "Cannot read the number of exports."); + for (uint32_t exportIndex = 0; exportIndex < numberOfExports; ++exportIndex) { + String exportName; + READ_STRING_OR_FAIL(exportName, "Cannot read the function export name."); + uint32_t functionIndex; + READ_COMPACT_UINT32_OR_FAIL(functionIndex, "Cannot read the function index."); + FAIL_IF_FALSE(functionIndex < m_module->functionDeclarations().size(), "The function index is incorrect."); + Identifier identifier = Identifier::fromString(&m_vm, exportName); + m_module->putDirect(m_vm, identifier, m_module->functions()[functionIndex].get()); + } + break; + } + default: + ASSERT_NOT_REACHED(); + } +} + +void WASMModuleParser::getImportedValue(ExecState* exec, const String& importName, JSValue& value) +{ + FAIL_IF_FALSE(m_imports, "Accessing property of non-object."); + Identifier identifier = Identifier::fromString(&m_vm, importName); + PropertySlot slot(m_imports.get(), PropertySlot::InternalMethodType::Get); + if (!m_imports->getPropertySlot(exec, identifier, slot)) + FAIL_WITH_MESSAGE("Can't find a property named \"" + importName + '"'); + FAIL_IF_FALSE(slot.isValue(), "\"" + importName + "\" is not a data property."); + // We only retrieve data properties. So, this does not cause any user-observable effect. + value = slot.getValue(exec, identifier); +} + +JSWASMModule* parseWebAssembly(ExecState* exec, const SourceCode& source, JSObject* imports, JSArrayBuffer* arrayBuffer, String& errorMessage) +{ + WASMModuleParser moduleParser(exec->vm(), exec->lexicalGlobalObject(), source, imports, arrayBuffer); + return moduleParser.parse(exec, errorMessage); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WASMModuleParser.h b/Source/JavaScriptCore/wasm/WASMModuleParser.h new file mode 100644 index 000000000..5017bc1ce --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMModuleParser.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WASMModuleParser_h +#define WASMModuleParser_h + +#if ENABLE(WEBASSEMBLY) + +#include "Strong.h" +#include "WASMReader.h" +#include <wtf/text/WTFString.h> + +namespace JSC { + +class ExecState; +class JSArrayBuffer; +class JSGlobalObject; +class JSWASMModule; +class SourceCode; +class VM; + +class WASMModuleParser { +public: + WASMModuleParser(VM&, JSGlobalObject*, const SourceCode&, JSObject* imports, JSArrayBuffer*); + JSWASMModule* parse(ExecState*, String& errorMessage); + +private: + void parseModule(ExecState*); + void parseConstantPoolSection(); + void parseSignatureSection(); + void parseFunctionImportSection(ExecState*); + void parseGlobalSection(ExecState*); + void parseFunctionDeclarationSection(); + void parseFunctionPointerTableSection(); + void parseFunctionDefinitionSection(); + void parseFunctionDefinition(size_t functionIndex); + void parseExportSection(); + void getImportedValue(ExecState*, const String& importName, JSValue&); + + VM& m_vm; + Strong<JSGlobalObject> m_globalObject; + const SourceCode& m_source; + Strong<JSObject> m_imports; + WASMReader m_reader; + Strong<JSWASMModule> m_module; + String m_errorMessage; +}; + +JS_EXPORT_PRIVATE JSWASMModule* parseWebAssembly(ExecState*, const SourceCode&, JSObject* imports, JSArrayBuffer*, String& errorMessage); + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // WASMModuleParser_h diff --git a/Source/JavaScriptCore/wasm/WASMReader.cpp b/Source/JavaScriptCore/wasm/WASMReader.cpp new file mode 100644 index 000000000..f6ac8e2fc --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMReader.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ========================================================================= + * + * Copyright (c) 2015 by the repository authors of + * WebAssembly/polyfill-prototype-1. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include "WASMReader.h" + +#if ENABLE(WEBASSEMBLY) + +#include <wtf/text/StringBuilder.h> + +#define CHECK_READ(length) do { if (m_cursor + length > m_buffer.end()) return false; } while (0) + +namespace JSC { + +bool WASMReader::readUInt32(uint32_t& result) +{ + CHECK_READ(4); + result = m_cursor[0] | m_cursor[1] << 8 | m_cursor[2] << 16 | m_cursor[3] << 24; + m_cursor += 4; + return true; +} + +bool WASMReader::readFloat(float& result) +{ + CHECK_READ(4); + union { + uint8_t bytes[4]; + float floatValue; + } u = { +#if CPU(BIG_ENDIAN) + { m_cursor[3], m_cursor[2], m_cursor[1], m_cursor[0] } +#else + { m_cursor[0], m_cursor[1], m_cursor[2], m_cursor[3] } +#endif + }; + result = u.floatValue; + m_cursor += 4; + return true; +} + +bool WASMReader::readDouble(double& result) +{ + CHECK_READ(8); + union { + uint8_t bytes[8]; + double doubleValue; + } u = { +#if CPU(BIG_ENDIAN) + { m_cursor[7], m_cursor[6], m_cursor[5], m_cursor[4], m_cursor[3], m_cursor[2], m_cursor[1], m_cursor[0] } +#else + { m_cursor[0], m_cursor[1], m_cursor[2], m_cursor[3], m_cursor[4], m_cursor[5], m_cursor[6], m_cursor[7] } +#endif + }; + result = u.doubleValue; + m_cursor += 8; + return true; +} + +bool WASMReader::readCompactInt32(uint32_t& result) +{ + uint32_t sum = 0; + unsigned shift = 0; + do { + CHECK_READ(1); + uint8_t byte = *m_cursor++; + if (byte < 0x80) { + sum |= byte << shift; + int signExtend = (32 - 7) - shift; + if (signExtend > 0) + result = int32_t(sum) << signExtend >> signExtend; + else + result = int32_t(sum); + return true; + } + sum |= (byte & firstSevenBitsMask) << shift; + shift += 7; + } while (shift < 35); + return false; +} + +bool WASMReader::readCompactUInt32(uint32_t& result) +{ + uint32_t sum = 0; + unsigned shift = 0; + do { + CHECK_READ(1); + uint32_t byte = *m_cursor++; + if (byte < 0x80) { + if ((shift == 28 && byte >= 0x10) || (shift && !byte)) + return false; + result = sum | (byte << shift); + return true; + } + sum |= (byte & firstSevenBitsMask) << shift; + shift += 7; + } while (shift < 35); + return false; +} + +bool WASMReader::readString(String& result) +{ + StringBuilder builder; + while (true) { + CHECK_READ(1); + char c = *m_cursor++; + if (!c) + break; + builder.append(c); + } + result = builder.toString(); + return true; +} + +bool WASMReader::readType(WASMType& result) +{ + return readByte<WASMType>(result, static_cast<uint8_t>(WASMType::NumberOfTypes)); +} + +bool WASMReader::readExpressionType(WASMExpressionType& result) +{ + return readByte<WASMExpressionType>(result, static_cast<uint8_t>(WASMExpressionType::NumberOfExpressionTypes)); +} + +bool WASMReader::readExportFormat(WASMExportFormat& result) +{ + return readByte<WASMExportFormat>(result, static_cast<uint8_t>(WASMExportFormat::NumberOfExportFormats)); +} + +template <class T> bool WASMReader::readByte(T& result, uint8_t numberOfValues) +{ + CHECK_READ(1); + uint8_t byte = *m_cursor++; + if (byte >= numberOfValues) + return false; + result = T(byte); + return true; +} + +bool WASMReader::readOpStatement(bool& hasImmediate, WASMOpStatement& op, WASMOpStatementWithImmediate& opWithImmediate, uint8_t& immediate) +{ + return readOp(hasImmediate, op, opWithImmediate, immediate, + static_cast<uint8_t>(WASMOpStatement::NumberOfWASMOpStatements), + static_cast<uint8_t>(WASMOpStatementWithImmediate::NumberOfWASMOpStatementWithImmediates)); +} + +bool WASMReader::readOpExpressionI32(bool& hasImmediate, WASMOpExpressionI32& op, WASMOpExpressionI32WithImmediate& opWithImmediate, uint8_t& immediate) +{ + return readOp(hasImmediate, op, opWithImmediate, immediate, + static_cast<uint8_t>(WASMOpExpressionI32::NumberOfWASMOpExpressionI32s), + static_cast<uint8_t>(WASMOpExpressionI32WithImmediate::NumberOfWASMOpExpressionI32WithImmediates)); +} + +bool WASMReader::readOpExpressionF32(bool& hasImmediate, WASMOpExpressionF32& op, WASMOpExpressionF32WithImmediate& opWithImmediate, uint8_t& immediate) +{ + return readOp(hasImmediate, op, opWithImmediate, immediate, + static_cast<uint8_t>(WASMOpExpressionF32::NumberOfWASMOpExpressionF32s), + static_cast<uint8_t>(WASMOpExpressionF32WithImmediate::NumberOfWASMOpExpressionF32WithImmediates)); +} + +bool WASMReader::readOpExpressionF64(bool& hasImmediate, WASMOpExpressionF64& op, WASMOpExpressionF64WithImmediate& opWithImmediate, uint8_t& immediate) +{ + return readOp(hasImmediate, op, opWithImmediate, immediate, + static_cast<uint8_t>(WASMOpExpressionF64::NumberOfWASMOpExpressionF64s), + static_cast<uint8_t>(WASMOpExpressionF64WithImmediate::NumberOfWASMOpExpressionF64WithImmediates)); +} + +bool WASMReader::readOpExpressionVoid(WASMOpExpressionVoid& op) +{ + return readByte<WASMOpExpressionVoid>(op, + static_cast<uint8_t>(WASMOpExpressionVoid::NumberOfWASMOpExpressionVoids)); +} + +bool WASMReader::readVariableTypes(bool& hasImmediate, WASMVariableTypes& variableTypes, WASMVariableTypesWithImmediate& variableTypesWithImmediate, uint8_t& immediate) +{ + return readOp(hasImmediate, variableTypes, variableTypesWithImmediate, immediate, + static_cast<uint8_t>(WASMVariableTypes::NumberOfVariableTypes), + static_cast<uint8_t>(WASMVariableTypesWithImmediate::NumberOfVariableTypesWithImmediates)); +} + +template <class T, class TWithImmediate> +bool WASMReader::readOp(bool& hasImmediate, T& op, TWithImmediate& opWithImmediate, uint8_t& immediate, uint8_t numberOfValues, uint8_t numberOfValuesWithImmediate) +{ + CHECK_READ(1); + uint8_t byte = *m_cursor++; + + if (!(byte & hasImmediateInOpFlag)) { + if (byte >= numberOfValues) + return false; + hasImmediate = false; + op = T(byte); + return true; + } + + uint8_t byteWithoutImmediate = (byte >> immediateBits) & (opWithImmediateLimit - 1); + if (byteWithoutImmediate >= numberOfValuesWithImmediate) + return false; + hasImmediate = true; + opWithImmediate = TWithImmediate(byteWithoutImmediate); + immediate = byte & (immediateLimit - 1); + return true; +} + +bool WASMReader::readSwitchCase(WASMSwitchCase& result) +{ + return readByte<WASMSwitchCase>(result, static_cast<uint8_t>(WASMSwitchCase::NumberOfSwitchCases)); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WASMReader.h b/Source/JavaScriptCore/wasm/WASMReader.h new file mode 100644 index 000000000..83dc29681 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WASMReader.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WASMReader_h +#define WASMReader_h + +#if ENABLE(WEBASSEMBLY) + +#include "WASMConstants.h" +#include "WASMFormat.h" +#include <wtf/Vector.h> + +namespace JSC { + +class WASMReader { +public: + WASMReader(const Vector<uint8_t>& buffer) + : m_buffer(buffer) + , m_cursor(buffer.data()) + { + } + + unsigned offset() const { return m_cursor - m_buffer.data(); } + void setOffset(unsigned offset) { m_cursor = m_buffer.data() + offset; } + + bool readUInt32(uint32_t& result); + bool readFloat(float& result); + bool readDouble(double& result); + bool readCompactInt32(uint32_t& result); + bool readCompactUInt32(uint32_t& result); + bool readString(String& result); + bool readType(WASMType& result); + bool readExpressionType(WASMExpressionType& result); + bool readExportFormat(WASMExportFormat& result); + bool readOpStatement(bool& hasImmediate, WASMOpStatement&, WASMOpStatementWithImmediate&, uint8_t& immediate); + bool readOpExpressionI32(bool& hasImmediate, WASMOpExpressionI32&, WASMOpExpressionI32WithImmediate&, uint8_t& immediate); + bool readOpExpressionF32(bool& hasImmediate, WASMOpExpressionF32&, WASMOpExpressionF32WithImmediate&, uint8_t& immediate); + bool readOpExpressionF64(bool& hasImmediate, WASMOpExpressionF64&, WASMOpExpressionF64WithImmediate&, uint8_t& immediate); + bool readOpExpressionVoid(WASMOpExpressionVoid&); + bool readVariableTypes(bool& hasImmediate, WASMVariableTypes&, WASMVariableTypesWithImmediate&, uint8_t& immediate); + bool readSwitchCase(WASMSwitchCase& result); + +private: + static const uint32_t firstSevenBitsMask = 0x7f; + + template <class T> bool readByte(T& result, uint8_t numberOfValues); + template <class T, class TWithImmediate> bool readOp(bool& hasImmediate, T&, TWithImmediate&, uint8_t& immediate, uint8_t numberOfValues, uint8_t numberOfValuesWithImmediate); + + const Vector<uint8_t>& m_buffer; + const uint8_t* m_cursor; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + +#endif // WASMReader_h |