Source/JavaScriptCore/ChangeLog

 12015-09-17 Sukolsak Sakshuwong <sukolsak@gmail.com>
 2
 3 Convert return values from JavaScript functions to the expected types in WebAssembly
 4 https://bugs.webkit.org/show_bug.cgi?id=149200
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 When a WebAssembly function calls a JavaScript function, there is no
 9 guarantee that the JavaScript function will always return values of the
 10 type we expect. This patch converts the return values to the expected
 11 types.
 12
 13 (The reverse is also true: When a WebAssembly function is called from a
 14 JavaScript function, there is no guarantee that the arguments to the
 15 WebAssembly function will always be of the types we expect. We have
 16 fixed this in Bug 149033.)
 17
 18 We don't need to type check the return values if the callee is a
 19 WebAssembly function. We don't need to type check the arguments if the
 20 caller is a WebAssembly function. This optimization will be
 21 implemented in the future.
 22
 23 * tests/stress/wasm-type-conversion.js:
 24 * tests/stress/wasm/type-conversion.wasm:
 25 * wasm/WASMFunctionCompiler.h:
 26 (JSC::WASMFunctionCompiler::startFunction):
 27 (JSC::WASMFunctionCompiler::buildReturn):
 28 (JSC::WASMFunctionCompiler::boxArgumentsAndAdjustStackPointer):
 29 (JSC::WASMFunctionCompiler::callAndUnboxResult):
 30 (JSC::WASMFunctionCompiler::convertValueToInt32):
 31 (JSC::WASMFunctionCompiler::convertValueToDouble):
 32 (JSC::WASMFunctionCompiler::convertDoubleToValue):
 33 (JSC::WASMFunctionCompiler::loadValueAndConvertToInt32): Deleted.
 34 (JSC::WASMFunctionCompiler::loadValueAndConvertToDouble): Deleted.
 35 * wasm/WASMFunctionParser.cpp:
 36 (JSC::WASMFunctionParser::parseExpressionI32):
 37 (JSC::WASMFunctionParser::parseExpressionF32):
 38 (JSC::WASMFunctionParser::parseExpressionF64):
 39 (JSC::WASMFunctionParser::parseCallInternalExpressionI32): Deleted.
 40 * wasm/WASMFunctionParser.h:
 41
1422015-09-16 Sukolsak Sakshuwong <sukolsak@gmail.com>
243
344 Implement indirect calls in WebAssembly

Source/JavaScriptCore/tests/stress/wasm-type-conversion.js

@@function shouldBe(actual, expected) {
88/*
99wasm/type-conversion.wasm is generated by pack-asmjs <https://github.com/WebAssembly/polyfill-prototype-1> from the following script:
1010
11 function asmModule(global, env, buffer) {
 11function asmModule(global, imports, buffer) {
1212 "use asm";
1313
1414 var fround = global.Math.fround;
 15 var getInt32 = imports.getInt32;
 16 var getDouble = imports.getDouble;
 17 var getString = imports.getString;
 18 var getBoolean = imports.getBoolean;
1519
1620 function takeAndReturnInt32(x) {
1721 x = x | 0;
18  return x;
 22 return x | 0;
1923 }
2024
2125 function takeAndReturnFloat(x) {

@@function asmModule(global, env, buffer) {
2832 return x;
2933 }
3034
 35 function returnInt32FromInt32() {
 36 return getInt32() | 0;
 37 }
 38
 39 function returnInt32FromDouble() {
 40 return getDouble() | 0;
 41 }
 42
 43 function returnInt32FromString() {
 44 return getString() | 0;
 45 }
 46
 47 function returnInt32FromBoolean() {
 48 return getBoolean() | 0;
 49 }
 50
 51 function returnDoubleFromInt32() {
 52 return +getInt32();
 53 }
 54
 55 function returnDoubleFromDouble() {
 56 return +getDouble();
 57 }
 58
 59 function returnDoubleFromString() {
 60 return +getString();
 61 }
 62
 63 function returnDoubleFromBoolean() {
 64 return +getBoolean();
 65 }
 66
3167 return {
3268 takeAndReturnInt32: takeAndReturnInt32,
 69 takeAndReturnFloat: takeAndReturnFloat,
3370 takeAndReturnDouble: takeAndReturnDouble,
 71
 72 returnInt32FromInt32: returnInt32FromInt32,
 73 returnInt32FromDouble: returnInt32FromDouble,
 74 returnInt32FromString: returnInt32FromString,
 75 returnInt32FromBoolean: returnInt32FromBoolean,
 76 returnDoubleFromInt32: returnDoubleFromInt32,
 77 returnDoubleFromDouble: returnDoubleFromDouble,
 78 returnDoubleFromString: returnDoubleFromString,
 79 returnDoubleFromBoolean: returnDoubleFromBoolean,
3480 };
3581}
3682*/
3783
38 var module = loadWebAssembly("wasm/type-conversion.wasm");
 84var imports = {
 85 getInt32: () => 42,
 86 getDouble: () => 4.2,
 87 getString: () => "4.2",
 88 getBoolean: () => true,
 89};
 90var module = loadWebAssembly("wasm/type-conversion.wasm", imports);
3991
4092var two = {
4193 valueOf() { return 2; }

@@shouldBe(isNaN(module.takeAndReturnDouble("one")), true);
70122shouldBe(isNaN(module.takeAndReturnDouble({ })), true);
71123shouldBe(module.takeAndReturnDouble([2.5]), 2.5);
72124shouldBe(module.takeAndReturnDouble(two), 2);
 125
 126shouldBe(module.returnInt32FromInt32(), 42);
 127shouldBe(module.returnInt32FromDouble(), 4);
 128shouldBe(module.returnInt32FromString(), 4);
 129shouldBe(module.returnInt32FromBoolean(), 1);
 130shouldBe(module.returnDoubleFromInt32(), 42);
 131shouldBe(module.returnDoubleFromDouble(), 4.2);
 132shouldBe(module.returnDoubleFromString(), 4.2);
 133shouldBe(module.returnDoubleFromBoolean(), 1);

Source/JavaScriptCore/tests/stress/wasm/type-conversion.wasm

Binary file, nothing to see here

Source/JavaScriptCore/wasm/WASMFunctionCompiler.h

@@public:
106106
107107 unsigned localIndex = 0;
108108 for (size_t i = 0; i < arguments.size(); ++i) {
 109 // FIXME: No need to do type conversion if the caller is a WebAssembly function.
109110 Address address(GPRInfo::callFrameRegister, CallFrame::argumentOffset(i) * sizeof(Register));
110  switch (arguments[i]) {
111  case WASMType::I32:
112111#if USE(JSVALUE64)
113  loadValueAndConvertToInt32(address, GPRInfo::regT0);
 112 JSValueRegs valueRegs(GPRInfo::regT0);
114113#else
115  loadValueAndConvertToInt32(address, GPRInfo::regT0, GPRInfo::regT1);
 114 JSValueRegs valueRegs(GPRInfo::regT1, GPRInfo::regT0);
116115#endif
 116 loadValue(address, valueRegs);
 117 switch (arguments[i]) {
 118 case WASMType::I32:
 119 convertValueToInt32(valueRegs, GPRInfo::regT0);
117120 store32(GPRInfo::regT0, localAddress(localIndex++));
118121 break;
119122 case WASMType::F32:
120123 case WASMType::F64:
121124#if USE(JSVALUE64)
122  loadValueAndConvertToDouble(address, FPRInfo::fpRegT0, GPRInfo::regT0, GPRInfo::regT1);
 125 convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::regT1);
123126#else
124  loadValueAndConvertToDouble(address, FPRInfo::fpRegT0, GPRInfo::regT0, GPRInfo::regT1, GPRInfo::regT2, FPRInfo::fpRegT1);
 127 convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::regT2, FPRInfo::fpRegT1);
125128#endif
126129 if (arguments[i] == WASMType::F32)
127130 convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0);

@@public:
276279 loadDouble(temporaryAddress(m_tempStackTop - 1), FPRInfo::fpRegT0);
277280 if (returnType == WASMExpressionType::F32)
278281 convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
279 #if USE(JSVALUE64)
280  boxDouble(FPRInfo::fpRegT0, GPRInfo::returnValueGPR);
281 #else
282  boxDouble(FPRInfo::fpRegT0, GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
283 #endif
 282 convertDoubleToValue(FPRInfo::fpRegT0, returnValueRegs);
284283 m_tempStackTop--;
285284 break;
286285 case WASMExpressionType::Void:

@@private:
919918
920919 for (size_t i = 0; i < argumentCount; ++i) {
921920 Address address(GPRInfo::callFrameRegister, (stackOffset + CallFrame::argumentOffset(i)) * sizeof(Register));
 921#if USE(JSVALUE64)
 922 JSValueRegs valueRegs(GPRInfo::regT0);
 923#else
 924 JSValueRegs valueRegs(GPRInfo::regT1, GPRInfo::regT0);
 925#endif
922926 switch (arguments[i]) {
923927 case WASMType::I32:
924928 load32(temporaryAddress(m_tempStackTop - argumentCount + i), GPRInfo::regT0);

@@private:
930934 store32(TrustedImm32(JSValue::Int32Tag), address.withOffset(TagOffset));
931935#endif
932936 break;
 937 case WASMType::F32:
 938 case WASMType::F64:
 939 loadDouble(temporaryAddress(m_tempStackTop - argumentCount + i), FPRInfo::fpRegT0);
 940 if (arguments[i] == WASMType::F32)
 941 convertFloatToDouble(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
 942 convertDoubleToValue(FPRInfo::fpRegT0, valueRegs);
 943 storeValue(valueRegs, address);
 944 break;
933945 default:
934946 ASSERT_NOT_REACHED();
935947 }

@@private:
969981 addPtr(TrustedImm32(-WTF::roundUpToMultipleOf(stackAlignmentRegisters(), m_stackHeight) * sizeof(StackSlot) - maxFrameExtentForSlowPathCall), GPRInfo::callFrameRegister, stackPointerRegister);
970982 checkStackPointerAlignment();
971983
 984 // FIXME: No need to do type conversion if the callee is a WebAssembly function.
 985#if USE(JSVALUE64)
 986 JSValueRegs valueRegs(GPRInfo::returnValueGPR);
 987#else
 988 JSValueRegs valueRegs(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR);
 989#endif
972990 switch (returnType) {
973991 case WASMExpressionType::I32:
974  store32(GPRInfo::returnValueGPR, temporaryAddress(m_tempStackTop++));
 992 convertValueToInt32(valueRegs, GPRInfo::regT0);
 993 store32(GPRInfo::regT0, temporaryAddress(m_tempStackTop++));
 994 break;
 995 case WASMExpressionType::F32:
 996 case WASMExpressionType::F64:
 997#if USE(JSVALUE64)
 998 convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::nonPreservedNonReturnGPR);
 999#else
 1000 convertValueToDouble(valueRegs, FPRInfo::fpRegT0, GPRInfo::nonPreservedNonReturnGPR, FPRInfo::fpRegT1);
 1001#endif
 1002 if (returnType == WASMExpressionType::F32)
 1003 convertDoubleToFloat(FPRInfo::fpRegT0, FPRInfo::fpRegT0);
 1004 storeDouble(FPRInfo::fpRegT0, temporaryAddress(m_tempStackTop++));
9751005 break;
9761006 case WASMExpressionType::Void:
9771007 break;

@@private:
9811011 }
9821012
9831013#if USE(JSVALUE64)
984  void loadValueAndConvertToInt32(Address address, GPRReg dst)
 1014 void convertValueToInt32(JSValueRegs valueRegs, GPRReg dst)
9851015 {
986  JSValueRegs tempRegs(dst);
987  loadValue(address, tempRegs);
988  Jump checkJSInt32 = branchIfInt32(tempRegs);
 1016 Jump checkJSInt32 = branchIfInt32(valueRegs);
9891017
990  callOperation(operationConvertJSValueToInt32, dst, dst);
 1018 callOperation(operationConvertJSValueToInt32, valueRegs.gpr(), valueRegs.gpr());
9911019
9921020 checkJSInt32.link(this);
 1021 move(valueRegs.gpr(), dst);
9931022 }
9941023
995  void loadValueAndConvertToDouble(Address address, FPRReg dst, GPRReg scratch1, GPRReg scratch2)
 1024 void convertValueToDouble(JSValueRegs valueRegs, FPRReg dst, GPRReg scratch)
9961025 {
997  JSValueRegs tempRegs(scratch1);
998  loadValue(address, tempRegs);
999  Jump checkJSInt32 = branchIfInt32(tempRegs);
1000  Jump checkJSNumber = branchIfNumber(tempRegs, scratch2);
 1026 Jump checkJSInt32 = branchIfInt32(valueRegs);
 1027 Jump checkJSNumber = branchIfNumber(valueRegs, scratch);
10011028 JumpList end;
10021029
1003  callOperation(operationConvertJSValueToDouble, tempRegs.gpr(), dst);
 1030 callOperation(operationConvertJSValueToDouble, valueRegs.gpr(), dst);
10041031 end.append(jump());
10051032
10061033 checkJSInt32.link(this);
1007  convertInt32ToDouble(tempRegs.gpr(), dst);
 1034 convertInt32ToDouble(valueRegs.gpr(), dst);
10081035 end.append(jump());
10091036
10101037 checkJSNumber.link(this);
1011  unboxDoubleWithoutAssertions(tempRegs.gpr(), dst);
 1038 unboxDoubleWithoutAssertions(valueRegs.gpr(), dst);
10121039 end.link(this);
10131040 }
10141041#else
1015  void loadValueAndConvertToInt32(Address address, GPRReg dst, GPRReg scratch)
 1042 void convertValueToInt32(JSValueRegs valueRegs, GPRReg dst)
10161043 {
1017  JSValueRegs tempRegs(scratch, dst);
1018  loadValue(address, tempRegs);
1019  Jump checkJSInt32 = branchIfInt32(tempRegs);
 1044 Jump checkJSInt32 = branchIfInt32(valueRegs);
10201045
1021  callOperation(operationConvertJSValueToInt32, tempRegs.tagGPR(), tempRegs.payloadGPR(), dst);
 1046 callOperation(operationConvertJSValueToInt32, valueRegs.tagGPR(), valueRegs.payloadGPR(), valueRegs.payloadGPR());
10221047
10231048 checkJSInt32.link(this);
 1049 move(valueRegs.payloadGPR(), dst);
10241050 }
10251051
1026  void loadValueAndConvertToDouble(Address address, FPRReg dst, GPRReg scratch1, GPRReg scratch2, GPRReg scratch3, FPRReg fpScratch)
 1052 void convertValueToDouble(JSValueRegs valueRegs, FPRReg dst, GPRReg scratch, FPRReg fpScratch)
10271053 {
1028  JSValueRegs tempRegs(scratch2, scratch1);
1029  loadValue(address, tempRegs);
1030  Jump checkJSInt32 = branchIfInt32(tempRegs);
1031  Jump checkJSNumber = branchIfNumber(tempRegs, scratch3);
 1054 Jump checkJSInt32 = branchIfInt32(valueRegs);
 1055 Jump checkJSNumber = branchIfNumber(valueRegs, scratch);
10321056 JumpList end;
10331057
1034  callOperation(operationConvertJSValueToDouble, tempRegs.tagGPR(), tempRegs.payloadGPR(), dst);
 1058 callOperation(operationConvertJSValueToDouble, valueRegs.tagGPR(), valueRegs.payloadGPR(), dst);
10351059 end.append(jump());
10361060
10371061 checkJSInt32.link(this);
1038  convertInt32ToDouble(tempRegs.payloadGPR(), dst);
 1062 convertInt32ToDouble(valueRegs.payloadGPR(), dst);
10391063 end.append(jump());
10401064
10411065 checkJSNumber.link(this);
1042  unboxDouble(tempRegs.tagGPR(), tempRegs.payloadGPR(), dst, fpScratch);
 1066 unboxDouble(valueRegs.tagGPR(), valueRegs.payloadGPR(), dst, fpScratch);
10431067 end.link(this);
10441068 }
10451069#endif
10461070
 1071 void convertDoubleToValue(FPRReg fpr, JSValueRegs valueRegs)
 1072 {
 1073#if USE(JSVALUE64)
 1074 boxDouble(fpr, valueRegs.gpr());
 1075#else
 1076 boxDouble(fpr, valueRegs.tagGPR(), valueRegs.payloadGPR());
 1077#endif
 1078 }
 1079
10471080 JSWASMModule* m_module;
10481081 unsigned m_stackHeight;
10491082 unsigned m_numberOfLocals;

Source/JavaScriptCore/wasm/WASMFunctionParser.cpp

@@ContextExpression WASMFunctionParser::parseExpressionI32(Context& context)
529529 case WASMOpExpressionI32::GetGlobal:
530530 return parseGetGlobalExpressionI32(context);
531531 case WASMOpExpressionI32::CallInternal:
532  return parseCallInternalExpressionI32(context);
 532 return parseCallInternal(context, WASMExpressionType::I32);
533533 case WASMOpExpressionI32::CallIndirect:
534534 return parseCallIndirect(context, WASMExpressionType::I32);
535535 case WASMOpExpressionI32::CallImport:

@@ContextExpression WASMFunctionParser::parseGetGlobalExpressionI32(Context& conte
681681}
682682
683683template <class Context>
684 ContextExpression WASMFunctionParser::parseCallInternalExpressionI32(Context& context)
685 {
686  return parseCallInternal(context, WASMExpressionType::I32);
687 }
688 
689 template <class Context>
690684ContextExpression WASMFunctionParser::parseUnaryExpressionI32(Context& context, WASMOpExpressionI32 op)
691685{
692686 ContextExpression expression = parseExpressionI32(context);

@@ContextExpression WASMFunctionParser::parseExpressionF32(Context& context)
752746 return parseGetLocalExpressionF32(context);
753747 case WASMOpExpressionF32::GetGlobal:
754748 return parseGetGlobalExpressionF32(context);
 749 case WASMOpExpressionF32::CallInternal:
 750 return parseCallInternal(context, WASMExpressionType::F32);
755751 case WASMOpExpressionF32::CallIndirect:
756752 return parseCallIndirect(context, WASMExpressionType::F32);
757753 case WASMOpExpressionF32::Negate:

@@ContextExpression WASMFunctionParser::parseExpressionF32(Context& context)
771767 case WASMOpExpressionF32::LoadWithOffset:
772768 case WASMOpExpressionF32::Store:
773769 case WASMOpExpressionF32::StoreWithOffset:
774  case WASMOpExpressionF32::CallInternal:
775770 case WASMOpExpressionF32::Conditional:
776771 case WASMOpExpressionF32::Comma:
777772 case WASMOpExpressionF32::FromS32:

@@ContextExpression WASMFunctionParser::parseExpressionF64(Context& context)
880875 return parseGetLocalExpressionF64(context);
881876 case WASMOpExpressionF64::GetGlobal:
882877 return parseGetGlobalExpressionF64(context);
 878 case WASMOpExpressionF64::CallInternal:
 879 return parseCallInternal(context, WASMExpressionType::F64);
883880 case WASMOpExpressionF64::CallImport:
884881 return parseCallImport(context, WASMExpressionType::F64);
885882 case WASMOpExpressionF64::CallIndirect:

@@ContextExpression WASMFunctionParser::parseExpressionF64(Context& context)
890887 case WASMOpExpressionF64::LoadWithOffset:
891888 case WASMOpExpressionF64::Store:
892889 case WASMOpExpressionF64::StoreWithOffset:
893  case WASMOpExpressionF64::CallInternal:
894890 case WASMOpExpressionF64::Conditional:
895891 case WASMOpExpressionF64::Comma:
896892 case WASMOpExpressionF64::FromS32:

Source/JavaScriptCore/wasm/WASMFunctionParser.h

@@private:
8989 template <class Context> ContextExpression parseGetLocalExpressionI32(Context&, uint32_t localIndex);
9090 template <class Context> ContextExpression parseGetLocalExpressionI32(Context&);
9191 template <class Context> ContextExpression parseGetGlobalExpressionI32(Context&);
92  template <class Context> ContextExpression parseCallInternalExpressionI32(Context&);
9392 template <class Context> ContextExpression parseUnaryExpressionI32(Context&, WASMOpExpressionI32);
9493 template <class Context> ContextExpression parseBinaryExpressionI32(Context&, WASMOpExpressionI32);
9594 template <class Context> ContextExpression parseRelationalI32ExpressionI32(Context&, WASMOpExpressionI32);