From d0e52555f07a6f01a25114355007bc9095e00f6a Mon Sep 17 00:00:00 2001 From: bradnelson Date: Wed, 17 Aug 2016 10:22:09 -0700 Subject: [PATCH] [wasm] Support validation of asm.js modules with != 3 args. Our previous per-arch instantiation thunks for asm.js didn't support modules that had or were called with anything other than 3 arguments. Adding support for this. Addding a runtime test method to check if asm validation succeeded. Adding a test of validation with different argument count combinations. R=mstarzinger@chromium.org TEST=mjsunit/asm/asm-validator.js BUG= https://bugs.chromium.org/p/v8/issues/detail?id=4203 Review-Url: https://codereview.chromium.org/2229723002 Cr-Commit-Position: refs/heads/master@{#38688} --- src/builtins/arm/builtins-arm.cc | 35 ++++- src/builtins/arm64/builtins-arm64.cc | 35 ++++- src/builtins/ia32/builtins-ia32.cc | 36 ++++- src/builtins/mips/builtins-mips.cc | 36 ++++- src/builtins/mips64/builtins-mips64.cc | 35 ++++- src/builtins/ppc/builtins-ppc.cc | 34 ++++- src/builtins/s390/builtins-s390.cc | 35 ++++- src/builtins/x64/builtins-x64.cc | 36 ++++- src/builtins/x87/builtins-x87.cc | 36 ++++- src/compiler.cc | 7 +- src/objects-inl.h | 2 + src/objects.h | 4 + src/runtime/runtime-compiler.cc | 12 +- src/runtime/runtime-test.cc | 16 +++ src/runtime/runtime.h | 3 +- test/mjsunit/asm/asm-validation.js | 188 +++++++++++++++++++++++++ 16 files changed, 502 insertions(+), 48 deletions(-) create mode 100644 test/mjsunit/asm/asm-validation.js diff --git a/src/builtins/arm/builtins-arm.cc b/src/builtins/arm/builtins-arm.cc index 63f3fa97a6..0ee3010ed9 100644 --- a/src/builtins/arm/builtins-arm.cc +++ b/src/builtins/arm/builtins-arm.cc @@ -1449,6 +1449,8 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { Label failed; { FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve argument count for later compare. + __ Move(r4, r0); // Push the number of arguments to the callee. __ SmiTag(r0); __ push(r0); @@ -1459,17 +1461,40 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { // The function. __ push(r1); // Copy arguments from caller (stdlib, foreign, heap). - for (int i = 2; i >= 0; --i) { - __ ldr(r4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + - i * kPointerSize)); - __ push(r4); + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ cmp(r4, Operand(j)); + __ b(ne, &over); + } + for (int i = j - 1; i >= 0; --i) { + __ ldr(r4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + + i * kPointerSize)); + __ push(r4); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done); + __ bind(&over); + } } + __ bind(&args_done); + // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); // A smi 0 is returned on failure, an object on success. __ JumpIfSmi(r0, &failed); + + __ Drop(2); + __ pop(r4); + __ SmiUntag(r4); scope.GenerateLeaveFrame(); - __ Drop(4); + + __ add(r4, r4, Operand(1)); + __ Drop(r4); __ Ret(); __ bind(&failed); diff --git a/src/builtins/arm64/builtins-arm64.cc b/src/builtins/arm64/builtins-arm64.cc index 0691c525b3..6e6e802636 100644 --- a/src/builtins/arm64/builtins-arm64.cc +++ b/src/builtins/arm64/builtins-arm64.cc @@ -1446,23 +1446,48 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { Label failed; { FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve argument count for later compare. + __ Move(x4, x0); // Push a copy of the target function and the new target. __ SmiTag(x0); // Push another copy as a parameter to the runtime call. __ Push(x0, x1, x3, x1); // Copy arguments from caller (stdlib, foreign, heap). - for (int i = 2; i >= 0; --i) { - __ ldr(x4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + - i * kPointerSize)); - __ push(x4); + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ cmp(x4, Operand(j)); + __ B(ne, &over); + } + for (int i = j - 1; i >= 0; --i) { + __ ldr(x4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + + i * kPointerSize)); + __ push(x4); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done); + __ bind(&over); + } } + __ bind(&args_done); + // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); // A smi 0 is returned on failure, an object on success. __ JumpIfSmi(x0, &failed); + + __ Drop(2); + __ pop(x4); + __ SmiUntag(x4); scope.GenerateLeaveFrame(); - __ Drop(4); + + __ add(x4, x4, Operand(1)); + __ Drop(x4); __ Ret(); __ bind(&failed); diff --git a/src/builtins/ia32/builtins-ia32.cc b/src/builtins/ia32/builtins-ia32.cc index f5f81d2d78..f5eb7f6002 100644 --- a/src/builtins/ia32/builtins-ia32.cc +++ b/src/builtins/ia32/builtins-ia32.cc @@ -1010,6 +1010,8 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { Label failed; { FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve argument count for later compare. + __ mov(ecx, eax); // Push the number of arguments to the callee. __ SmiTag(eax); __ push(eax); @@ -1020,16 +1022,42 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { // The function. __ push(edi); // Copy arguments from caller (stdlib, foreign, heap). - for (int i = 2; i >= 0; --i) { - __ push(Operand( - ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize)); + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ cmp(ecx, Immediate(j)); + __ j(not_equal, &over, Label::kNear); + } + for (int i = j - 1; i >= 0; --i) { + __ Push(Operand( + ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize)); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done, Label::kNear); + __ bind(&over); + } } + __ bind(&args_done); + // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); // A smi 0 is returned on failure, an object on success. __ JumpIfSmi(eax, &failed, Label::kNear); + + __ Drop(2); + __ Pop(ecx); + __ SmiUntag(ecx); scope.GenerateLeaveFrame(); - __ ret(4 * kPointerSize); + + __ PopReturnAddressTo(ebx); + __ inc(ecx); + __ lea(esp, Operand(esp, ecx, times_pointer_size, 0)); + __ PushReturnAddressFrom(ebx); + __ ret(0); __ bind(&failed); // Restore target function and new target. diff --git a/src/builtins/mips/builtins-mips.cc b/src/builtins/mips/builtins-mips.cc index f460fec7d4..bd0e7e252d 100644 --- a/src/builtins/mips/builtins-mips.cc +++ b/src/builtins/mips/builtins-mips.cc @@ -1444,23 +1444,47 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { Label failed; { FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve argument count for later compare. + __ Move(t4, a0); // Push a copy of the target function and the new target. // Push function as parameter to the runtime call. __ SmiTag(a0); __ Push(a0, a1, a3, a1); // Copy arguments from caller (stdlib, foreign, heap). - for (int i = 2; i >= 0; --i) { - __ lw(a3, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + - i * kPointerSize)); - __ push(a3); + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ Branch(&over, ne, t4, Operand(j)); + } + for (int i = j - 1; i >= 0; --i) { + __ lw(t4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + + i * kPointerSize)); + __ push(t4); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done); + __ bind(&over); + } } + __ bind(&args_done); + // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); // A smi 0 is returned on failure, an object on success. - __ JumpIfSmi(a0, &failed); + __ JumpIfSmi(v0, &failed); + + __ Drop(2); + __ pop(t4); + __ SmiUntag(t4); scope.GenerateLeaveFrame(); - __ Drop(4); + + __ Addu(t4, t4, Operand(1)); + __ Lsa(sp, sp, t4, kPointerSizeLog2); __ Ret(); __ bind(&failed); diff --git a/src/builtins/mips64/builtins-mips64.cc b/src/builtins/mips64/builtins-mips64.cc index 1a190f5a99..cbbe541526 100644 --- a/src/builtins/mips64/builtins-mips64.cc +++ b/src/builtins/mips64/builtins-mips64.cc @@ -1438,21 +1438,44 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { FrameScope scope(masm, StackFrame::INTERNAL); // Push a copy of the target function and the new target. // Push function as parameter to the runtime call. + __ Move(t2, a0); __ SmiTag(a0); __ Push(a0, a1, a3, a1); // Copy arguments from caller (stdlib, foreign, heap). - for (int i = 2; i >= 0; --i) { - __ lw(a3, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + - i * kPointerSize)); - __ push(a3); + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ Branch(&over, ne, t2, Operand(j)); + } + for (int i = j - 1; i >= 0; --i) { + __ lw(t2, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + + i * kPointerSize)); + __ push(t2); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done); + __ bind(&over); + } } + __ bind(&args_done); + // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); // A smi 0 is returned on failure, an object on success. - __ JumpIfSmi(a0, &failed); + __ JumpIfSmi(v0, &failed); + + __ Drop(2); + __ pop(t2); + __ SmiUntag(t2); scope.GenerateLeaveFrame(); - __ Drop(4); + + __ Addu(t2, t2, Operand(1)); + __ Lsa(sp, sp, t2, kPointerSizeLog2); __ Ret(); __ bind(&failed); diff --git a/src/builtins/ppc/builtins-ppc.cc b/src/builtins/ppc/builtins-ppc.cc index ef01f40b6e..b799819dbd 100644 --- a/src/builtins/ppc/builtins-ppc.cc +++ b/src/builtins/ppc/builtins-ppc.cc @@ -1458,23 +1458,47 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { Label failed; { FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve argument count for later compare. + __ Move(r5, r3); // Push a copy of the target function and the new target. // Push function as parameter to the runtime call. __ SmiTag(r3); __ Push(r3, r4, r6, r4); // Copy arguments from caller (stdlib, foreign, heap). - for (int i = 2; i >= 0; --i) { - __ LoadP(r4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + - i * kPointerSize)); - __ push(r4); + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ cmpi(r5, Operand(j)); + __ bne(&over); + } + for (int i = j - 1; i >= 0; --i) { + __ LoadP(r5, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + + i * kPointerSize)); + __ push(r5); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done); + __ bind(&over); + } } + __ bind(&args_done); + // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); // A smi 0 is returned on failure, an object on success. __ JumpIfSmi(r3, &failed); + + __ Drop(2); + __ pop(r5); + __ SmiUntag(r5); scope.GenerateLeaveFrame(); - __ Drop(4); + + __ Drop(r5); __ Ret(); __ bind(&failed); diff --git a/src/builtins/s390/builtins-s390.cc b/src/builtins/s390/builtins-s390.cc index aaa31bde27..4e7b585e32 100644 --- a/src/builtins/s390/builtins-s390.cc +++ b/src/builtins/s390/builtins-s390.cc @@ -1465,23 +1465,48 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { Label failed; { FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve argument count for later compare. + __ Move(r4, r2); // Push a copy of the target function and the new target. __ SmiTag(r2); // Push another copy as a parameter to the runtime call. __ Push(r2, r3, r5, r3); // Copy arguments from caller (stdlib, foreign, heap). - for (int i = 2; i >= 0; --i) { - __ LoadP(r4, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + - i * kPointerSize)); - __ push(r4); + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ CmpP(r4, Operand(j)); + __ b(ne, &over); + } + for (int i = j - 1; i >= 0; --i) { + __ LoadP(r9, MemOperand(fp, StandardFrameConstants::kCallerSPOffset + + i * kPointerSize)); + __ push(r9); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done); + __ bind(&over); + } } + __ bind(&args_done); + // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); // A smi 0 is returned on failure, an object on success. __ JumpIfSmi(r2, &failed); + + __ Drop(2); + __ pop(r4); + __ SmiUntag(r4); scope.GenerateLeaveFrame(); - __ Drop(4); + + __ AddP(r4, r4, Operand(1)); + __ Drop(r4, r7); __ Ret(); __ bind(&failed); diff --git a/src/builtins/x64/builtins-x64.cc b/src/builtins/x64/builtins-x64.cc index 87a7590875..944c784527 100644 --- a/src/builtins/x64/builtins-x64.cc +++ b/src/builtins/x64/builtins-x64.cc @@ -1062,6 +1062,8 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { Label failed; { FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve argument count for later compare. + __ movp(kScratchRegister, rax); // Push the number of arguments to the callee. __ Integer32ToSmi(rax, rax); __ Push(rax); @@ -1072,16 +1074,42 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { // The function. __ Push(rdi); // Copy arguments from caller (stdlib, foreign, heap). - for (int i = 2; i >= 0; --i) { - __ Push(Operand( - rbp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize)); + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ cmpp(kScratchRegister, Immediate(j)); + __ j(not_equal, &over, Label::kNear); + } + for (int i = j - 1; i >= 0; --i) { + __ Push(Operand( + rbp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize)); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done, Label::kNear); + __ bind(&over); + } } + __ bind(&args_done); + // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); // A smi 0 is returned on failure, an object on success. __ JumpIfSmi(rax, &failed, Label::kNear); + + __ Drop(2); + __ Pop(kScratchRegister); + __ SmiToInteger32(kScratchRegister, kScratchRegister); scope.GenerateLeaveFrame(); - __ ret(4 * kPointerSize); + + __ PopReturnAddressTo(rbx); + __ incp(kScratchRegister); + __ leap(rsp, Operand(rsp, kScratchRegister, times_pointer_size, 0)); + __ PushReturnAddressFrom(rbx); + __ ret(0); __ bind(&failed); // Restore target function and new target. diff --git a/src/builtins/x87/builtins-x87.cc b/src/builtins/x87/builtins-x87.cc index 4135c28596..bb7dc8380b 100644 --- a/src/builtins/x87/builtins-x87.cc +++ b/src/builtins/x87/builtins-x87.cc @@ -1011,6 +1011,8 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { Label failed; { FrameScope scope(masm, StackFrame::INTERNAL); + // Preserve argument count for later compare. + __ mov(ecx, eax); // Push the number of arguments to the callee. __ SmiTag(eax); __ push(eax); @@ -1021,16 +1023,42 @@ void Builtins::Generate_InstantiateAsmJs(MacroAssembler* masm) { // The function. __ push(edi); // Copy arguments from caller (stdlib, foreign, heap). - for (int i = 2; i >= 0; --i) { - __ push(Operand( - ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize)); + Label args_done; + for (int j = 0; j < 4; ++j) { + Label over; + if (j < 3) { + __ cmp(ecx, Immediate(j)); + __ j(not_equal, &over, Label::kNear); + } + for (int i = j - 1; i >= 0; --i) { + __ Push(Operand( + ebp, StandardFrameConstants::kCallerSPOffset + i * kPointerSize)); + } + for (int i = 0; i < 3 - j; ++i) { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } + if (j < 3) { + __ jmp(&args_done, Label::kNear); + __ bind(&over); + } } + __ bind(&args_done); + // Call runtime, on success unwind frame, and parent frame. __ CallRuntime(Runtime::kInstantiateAsmJs, 4); // A smi 0 is returned on failure, an object on success. __ JumpIfSmi(eax, &failed, Label::kNear); + + __ Drop(2); + __ Pop(ecx); + __ SmiUntag(ecx); scope.GenerateLeaveFrame(); - __ ret(4 * kPointerSize); + + __ PopReturnAddressTo(ebx); + __ inc(ecx); + __ lea(esp, Operand(esp, ecx, times_pointer_size, 0)); + __ PushReturnAddressFrom(ebx); + __ ret(0); __ bind(&failed); // Restore target function and new target. diff --git a/src/compiler.cc b/src/compiler.cc index 54ea359c65..4a7e275021 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -476,7 +476,8 @@ int CodeAndMetadataSize(CompilationInfo* info) { bool GenerateUnoptimizedCode(CompilationInfo* info) { bool success; EnsureFeedbackMetadata(info); - if (FLAG_validate_asm && info->scope()->asm_module()) { + if (FLAG_validate_asm && info->scope()->asm_module() && + !info->shared_info()->is_asm_wasm_broken()) { MaybeHandle wasm_data; wasm_data = AsmJs::ConvertAsmToWasm(info->parse_info()); if (!wasm_data.is_null()) { @@ -1436,7 +1437,9 @@ bool Compiler::EnsureDeoptimizationSupport(CompilationInfo* info) { // TODO(4280): For now we play it safe and remove the bytecode array when we // switch to baseline code. We might consider keeping around the bytecode so // that it can be used as the "source of truth" eventually. - if (!FLAG_ignition_preserve_bytecode) shared->ClearBytecodeArray(); + if (shared->HasBytecodeArray()) { + if (!FLAG_ignition_preserve_bytecode) shared->ClearBytecodeArray(); + } // The scope info might not have been set if a lazily compiled // function is inlined before being called for the first time. diff --git a/src/objects-inl.h b/src/objects-inl.h index 19da18e1f7..c736467ebe 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -6091,6 +6091,8 @@ BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_setter_function, kIsSetterFunction) BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_default_constructor, kIsDefaultConstructor) +BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_asm_wasm_broken, + kIsAsmWasmBroken) inline bool SharedFunctionInfo::is_resumable() const { return is_generator() || is_async(); diff --git a/src/objects.h b/src/objects.h index 1b0ded0aa8..debd292485 100644 --- a/src/objects.h +++ b/src/objects.h @@ -7307,6 +7307,9 @@ class SharedFunctionInfo: public HeapObject { // Whether this function was created from a FunctionDeclaration. DECL_BOOLEAN_ACCESSORS(is_declaration) + // Indicates that asm->wasm conversion failed and should not be re-attempted. + DECL_BOOLEAN_ACCESSORS(is_asm_wasm_broken) + inline FunctionKind kind(); inline void set_kind(FunctionKind kind); @@ -7579,6 +7582,7 @@ class SharedFunctionInfo: public HeapObject { kIsAsyncFunction, kDeserialized, kIsDeclaration, + kIsAsmWasmBroken, kCompilerHintsCount, // Pseudo entry }; // Add hints for other modes when they're added. diff --git a/src/runtime/runtime-compiler.cc b/src/runtime/runtime-compiler.cc index 862fefcd50..d75ecdd3c1 100644 --- a/src/runtime/runtime-compiler.cc +++ b/src/runtime/runtime-compiler.cc @@ -101,8 +101,18 @@ RUNTIME_FUNCTION(Runtime_InstantiateAsmJs) { return *result.ToHandleChecked(); } } - // Remove wasm data and return a smi 0 to indicate failure. + // Remove wasm data, mark as broken for asm->wasm, + // replace code with CompileLazy, and return a smi 0 to indicate failure. function->shared()->ClearAsmWasmData(); + function->shared()->set_is_asm_wasm_broken(true); + DCHECK(function->code() == + isolate->builtins()->builtin(Builtins::kInstantiateAsmJs)); + function->ReplaceCode(isolate->builtins()->builtin(Builtins::kCompileLazy)); + if (function->shared()->code() == + isolate->builtins()->builtin(Builtins::kInstantiateAsmJs)) { + function->shared()->ReplaceCode( + isolate->builtins()->builtin(Builtins::kCompileLazy)); + } return Smi::FromInt(0); } diff --git a/src/runtime/runtime-test.cc b/src/runtime/runtime-test.cc index 356c6c7717..2ee10d2bfe 100644 --- a/src/runtime/runtime-test.cc +++ b/src/runtime/runtime-test.cc @@ -662,6 +662,22 @@ RUNTIME_FUNCTION(Runtime_InNewSpace) { return isolate->heap()->ToBoolean(isolate->heap()->InNewSpace(obj)); } +RUNTIME_FUNCTION(Runtime_IsAsmWasmCode) { + SealHandleScope shs(isolate); + DCHECK(args.length() == 1); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); + + if (!function->shared()->HasAsmWasmData()) { + // Doesn't have wasm data. + return isolate->heap()->ToBoolean(false); + } + if (function->shared()->code() != + isolate->builtins()->builtin(Builtins::kInstantiateAsmJs)) { + // Hasn't been compiled yet. + return isolate->heap()->ToBoolean(false); + } + return isolate->heap()->ToBoolean(true); +} #define ELEMENTS_KIND_CHECK_RUNTIME_FUNCTION(Name) \ RUNTIME_FUNCTION(Runtime_Has##Name) { \ diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 627a9ce6d8..e4590ad4dd 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -885,7 +885,8 @@ namespace internal { F(HasFixedUint8ClampedElements, 1, 1) \ F(SpeciesProtector, 0, 1) \ F(SerializeWasmModule, 1, 1) \ - F(DeserializeWasmModule, 1, 1) + F(DeserializeWasmModule, 1, 1) \ + F(IsAsmWasmCode, 1, 1) #define FOR_EACH_INTRINSIC_TYPEDARRAY(F) \ F(ArrayBufferGetByteLength, 1, 1) \ diff --git a/test/mjsunit/asm/asm-validation.js b/test/mjsunit/asm/asm-validation.js new file mode 100644 index 0000000000..ed29ede25d --- /dev/null +++ b/test/mjsunit/asm/asm-validation.js @@ -0,0 +1,188 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --validate-asm --allow-natives-syntax + +function IsAlwaysOpt(module) { + return %GetOptimizationStatus(module) === 3; +} + +(function TestModuleArgs() { + function Module1(stdlib) { + "use asm"; + function foo() { } + return { foo: foo }; + } + function Module2(stdlib, ffi) { + "use asm"; + function foo() { } + return { foo: foo }; + } + function Module3(stdlib, ffi, heap) { + "use asm"; + function foo() { } + return { foo: foo }; + } + var modules = [Module1, Module2, Module3]; + var heap = new ArrayBuffer(1024 * 1024); + for (var i = 0; i < modules.length; ++i) { + print('Module' + (i + 1)); + var module = modules[i]; + // TODO(bradnelson): Support modules without the stdlib. + var m = module({}); + assertTrue(%IsAsmWasmCode(module) || IsAlwaysOpt(module)); + var m = module({}, {}); + assertTrue(%IsAsmWasmCode(module) || IsAlwaysOpt(module)); + var m = module({}, {}, heap); + assertTrue(%IsAsmWasmCode(module) || IsAlwaysOpt(module)); + var m = module({}, {}, heap, {}); + assertTrue(%IsAsmWasmCode(module) || IsAlwaysOpt(module)); + } +})(); + +(function TestBadModule() { + function Module(stdlib, ffi, heap) { + "use asm"; + function foo() { var y = 3; var x = 1 + y; return 123; } + return { foo: foo }; + } + var m = Module({}); + assertFalse(%IsAsmWasmCode(Module)); + assertEquals(123, m.foo()); +})(); + +(function TestBadArgTypes() { + function Module(a, b, c) { + "use asm"; + return {}; + } + var m = Module(1, 2, 3); + assertFalse(%IsAsmWasmCode(Module)); + assertEquals({}, m); +})(); + +(function TestBadArgTypesMismatch() { + function Module(a, b, c) { + "use asm"; + return {}; + } + var m = Module(1, 2); + assertFalse(%IsAsmWasmCode(Module)); + assertEquals({}, m); +})(); + +(function TestModuleNoStdlib() { + // TODO(bradnelson): + // Support modules like this if they don't use the whole stdlib. + function Module() { + "use asm"; + function foo() { return 123; } + return { foo: foo }; + } + var m = Module({}); + assertFalse(%IsAsmWasmCode(Module)); + assertEquals(123, m.foo()); +})(); + +(function TestModuleWith5() { + function Module(a, b, c, d, e) { + "use asm"; + function foo() { return 123; } + return { foo: foo }; + } + var heap = new ArrayBuffer(1024 * 1024); + var m = Module({}, {}, heap); + assertFalse(%IsAsmWasmCode(Module)); + assertEquals(123, m.foo()); +})(); + +(function TestModuleNoStdlibCall() { + function Module(stdlib, ffi, heap) { + "use asm"; + function foo() { return 123; } + return { foo: foo }; + } + // TODO(bradnelson): Support instantiation like this if stdlib is unused. + var m = Module(); + assertFalse(%IsAsmWasmCode(Module)); + assertEquals(123, m.foo()); +})(); + +(function TestModuleNew() { + function Module(stdlib, ffi, heap) { + "use asm"; + function foo() { return 123; } + return { foo: foo }; + } + var m = new Module({}, {}); + assertTrue(%IsAsmWasmCode(Module) || IsAlwaysOpt(Module)); + assertEquals(123, m.foo()); +})(); + +(function TestMultipleFailures() { + function Module(stdlib) { + "use asm"; + function foo() { return 123; } + return { foo: foo }; + } + var m1 = Module(1, 2, 3); + assertFalse(%IsAsmWasmCode(Module)); + var m2 = Module(1, 2, 3); + assertFalse(%IsAsmWasmCode(Module)); + assertEquals(123, m1.foo()); + assertEquals(123, m2.foo()); +})(); + +(function TestFailureThenSuccess() { + function MkModule() { + function Module(stdlib, ffi, heap) { + "use asm"; + function foo() { return 123; } + return { foo: foo }; + } + return Module; + } + var Module1 = MkModule(); + var Module2 = MkModule(); + var heap = new ArrayBuffer(1024 * 1024); + var m1 = Module1(1, 2, 3); + assertFalse(%IsAsmWasmCode(Module1)); + var m2 = Module2({}, {}, heap); + assertFalse(%IsAsmWasmCode(Module2)); + assertEquals(123, m1.foo()); + assertEquals(123, m2.foo()); +})(); + +(function TestSuccessThenFailure() { + function MkModule() { + function Module(stdlib, ffi, heap) { + "use asm"; + function foo() { return 123; } + return { foo: foo }; + } + return Module; + } + var Module1 = MkModule(); + var Module2 = MkModule(); + var heap = new ArrayBuffer(1024 * 1024); + var m1 = Module1({}, {}, heap); + assertTrue(%IsAsmWasmCode(Module1) || IsAlwaysOpt(Module1)); + var m2 = Module2(1, 2, 3); + assertFalse(%IsAsmWasmCode(Module2)); + assertEquals(123, m1.foo()); + assertEquals(123, m2.foo()); +})(); + +(function TestBoundFunction() { + function Module(stdlib, ffi, heap) { + "use asm"; + function foo() { return 123; } + return { foo: foo }; + } + var heap = new ArrayBuffer(1024 * 1024); + var ModuleBound = Module.bind(this, {}, {}, heap); + var m = ModuleBound(); + assertTrue(%IsAsmWasmCode(Module) || IsAlwaysOpt(Module)); + assertEquals(123, m.foo()); +})();