Commit aa912252 authored by jpp's avatar jpp Committed by Commit bot

V8. ASM-2-WASM. Migrates asm-wasm-builder to the new asm-typer.

BUG= https://bugs.chromium.org/p/v8/issues/detail?id=4203
TEST=mjsunit/wasm/*
LOG=N

Review-Url: https://codereview.chromium.org/2134333003
Cr-Commit-Position: refs/heads/master@{#37729}
parent 819fe046
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
#include "src/api-natives.h" #include "src/api-natives.h"
#include "src/api.h" #include "src/api.h"
#include "src/asmjs/asm-typer.h"
#include "src/asmjs/asm-wasm-builder.h" #include "src/asmjs/asm-wasm-builder.h"
#include "src/asmjs/typing-asm.h"
#include "src/assert-scope.h" #include "src/assert-scope.h"
#include "src/ast/ast.h" #include "src/ast/ast.h"
#include "src/ast/scopes.h" #include "src/ast/scopes.h"
...@@ -59,12 +59,8 @@ i::MaybeHandle<i::FixedArray> CompileModule( ...@@ -59,12 +59,8 @@ i::MaybeHandle<i::FixedArray> CompileModule(
MaybeHandle<FixedArray> AsmJs::ConvertAsmToWasm(ParseInfo* info) { MaybeHandle<FixedArray> AsmJs::ConvertAsmToWasm(ParseInfo* info) {
ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion"); ErrorThrower thrower(info->isolate(), "Asm.js -> WebAssembly conversion");
AsmTyper typer(info->isolate(), info->zone(), *(info->script()), wasm::AsmTyper typer(info->isolate(), info->zone(), *(info->script()),
info->literal()); info->literal());
typer.set_fixed_signature(true);
if (i::FLAG_enable_simd_asmjs) {
typer.set_allow_simd(true);
}
if (!typer.Validate()) { if (!typer.Validate()) {
DCHECK(!info->isolate()->has_pending_exception()); DCHECK(!info->isolate()->has_pending_exception());
PrintF("Validation of asm.js module failed: %s", typer.error_message()); PrintF("Validation of asm.js module failed: %s", typer.error_message());
......
This diff is collapsed.
...@@ -22,6 +22,7 @@ namespace v8 { ...@@ -22,6 +22,7 @@ namespace v8 {
namespace internal { namespace internal {
namespace wasm { namespace wasm {
class AsmType;
class AsmTyperHarnessBuilder; class AsmTyperHarnessBuilder;
class AsmTyper final { class AsmTyper final {
...@@ -313,6 +314,7 @@ class AsmTyper final { ...@@ -313,6 +314,7 @@ class AsmTyper final {
bool stack_overflow_ = false; bool stack_overflow_ = false;
ZoneMap<AstNode*, AsmType*> node_types_; ZoneMap<AstNode*, AsmType*> node_types_;
static const int kErrorMessageLimit = 100; static const int kErrorMessageLimit = 100;
AsmType* fround_type_;
char error_message_[kErrorMessageLimit]; char error_message_[kErrorMessageLimit];
DISALLOW_IMPLICIT_CONSTRUCTORS(AsmTyper); DISALLOW_IMPLICIT_CONSTRUCTORS(AsmTyper);
......
...@@ -173,6 +173,8 @@ class AsmFroundType final : public AsmFunctionType { ...@@ -173,6 +173,8 @@ class AsmFroundType final : public AsmFunctionType {
AsmType* ValidateCall(AsmType* return_type, AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override; const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
}; };
} // namespace } // namespace
...@@ -181,6 +183,7 @@ AsmType* AsmType::FroundType(Zone* zone) { ...@@ -181,6 +183,7 @@ AsmType* AsmType::FroundType(Zone* zone) {
return reinterpret_cast<AsmType*>(Fround); return reinterpret_cast<AsmType*>(Fround);
} }
// TODO(jpp): Remove this method.
AsmType* AsmFroundType::ValidateCall(AsmType* return_type, AsmType* AsmFroundType::ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) { const ZoneVector<AsmType*>& args) {
if (args.size() != 1) { if (args.size() != 1) {
...@@ -196,6 +199,21 @@ AsmType* AsmFroundType::ValidateCall(AsmType* return_type, ...@@ -196,6 +199,21 @@ AsmType* AsmFroundType::ValidateCall(AsmType* return_type,
return AsmType::Float(); return AsmType::Float();
} }
bool AsmFroundType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (args.size() != 1) {
return false;
}
auto* arg = args[0];
if (!arg->IsA(AsmType::Floatish()) && !arg->IsA(AsmType::DoubleQ()) &&
!arg->IsA(AsmType::Signed()) && !arg->IsA(AsmType::Unsigned())) {
return false;
}
return true;
}
namespace { namespace {
class AsmMinMaxType final : public AsmFunctionType { class AsmMinMaxType final : public AsmFunctionType {
public: public:
...@@ -228,6 +246,26 @@ class AsmMinMaxType final : public AsmFunctionType { ...@@ -228,6 +246,26 @@ class AsmMinMaxType final : public AsmFunctionType {
return ReturnType(); return ReturnType();
} }
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override {
if (!ReturnType()->IsExactly(return_type)) {
return false;
}
if (args.size() < 2) {
return false;
}
auto* arg_type = Arguments()[0];
for (size_t ii = 0; ii < Arguments().size(); ++ii) {
if (!args[ii]->IsA(arg_type)) {
return false;
}
}
return true;
}
}; };
} // namespace } // namespace
...@@ -249,6 +287,21 @@ AsmType* AsmFFIType::ValidateCall(AsmType* return_type, ...@@ -249,6 +287,21 @@ AsmType* AsmFFIType::ValidateCall(AsmType* return_type,
return return_type; return return_type;
} }
bool AsmFFIType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (return_type->IsExactly(AsmType::Float())) {
return false;
}
for (size_t ii = 0; ii < args.size(); ++ii) {
if (!args[ii]->IsA(AsmType::Extern())) {
return false;
}
}
return true;
}
AsmType* AsmFunctionType::ValidateCall(AsmType* return_type, AsmType* AsmFunctionType::ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) { const ZoneVector<AsmType*>& args) {
if (!return_type_->IsExactly(return_type)) { if (!return_type_->IsExactly(return_type)) {
...@@ -268,6 +321,25 @@ AsmType* AsmFunctionType::ValidateCall(AsmType* return_type, ...@@ -268,6 +321,25 @@ AsmType* AsmFunctionType::ValidateCall(AsmType* return_type,
return return_type_; return return_type_;
} }
bool AsmFunctionType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
if (!return_type_->IsExactly(return_type)) {
return false;
}
if (args_.size() != args.size()) {
return false;
}
for (size_t ii = 0; ii < args_.size(); ++ii) {
if (!args[ii]->IsA(args_[ii])) {
return false;
}
}
return true;
}
std::string AsmOverloadedFunctionType::Name() { std::string AsmOverloadedFunctionType::Name() {
std::string ret; std::string ret;
...@@ -294,6 +366,17 @@ AsmType* AsmOverloadedFunctionType::ValidateCall( ...@@ -294,6 +366,17 @@ AsmType* AsmOverloadedFunctionType::ValidateCall(
return AsmType::None(); return AsmType::None();
} }
bool AsmOverloadedFunctionType::CanBeInvokedWith(
AsmType* return_type, const ZoneVector<AsmType*>& args) {
for (size_t ii = 0; ii < overloads_.size(); ++ii) {
if (overloads_[ii]->AsCallableType()->CanBeInvokedWith(return_type, args)) {
return true;
}
}
return false;
}
void AsmOverloadedFunctionType::AddOverload(AsmType* overload) { void AsmOverloadedFunctionType::AddOverload(AsmType* overload) {
DCHECK(overload->AsFunctionType() != nullptr); DCHECK(overload->AsFunctionType() != nullptr);
overloads_.push_back(overload); overloads_.push_back(overload);
...@@ -314,6 +397,11 @@ AsmType* AsmFunctionTableType::ValidateCall(AsmType* return_type, ...@@ -314,6 +397,11 @@ AsmType* AsmFunctionTableType::ValidateCall(AsmType* return_type,
return signature_->AsCallableType()->ValidateCall(return_type, args); return signature_->AsCallableType()->ValidateCall(return_type, args);
} }
bool AsmFunctionTableType::CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) {
return signature_->AsCallableType()->CanBeInvokedWith(return_type, args);
}
} // namespace wasm } // namespace wasm
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -110,6 +110,9 @@ class AsmCallableType : public ZoneObject { ...@@ -110,6 +110,9 @@ class AsmCallableType : public ZoneObject {
virtual AsmType* ValidateCall(AsmType* return_type, virtual AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) = 0; const ZoneVector<AsmType*>& args) = 0;
virtual bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) = 0;
#define DECLARE_CAST(CamelName) \ #define DECLARE_CAST(CamelName) \
virtual Asm##CamelName* As##CamelName() { return nullptr; } virtual Asm##CamelName* As##CamelName() { return nullptr; }
FOR_EACH_ASM_CALLABLE_TYPE_LIST(DECLARE_CAST) FOR_EACH_ASM_CALLABLE_TYPE_LIST(DECLARE_CAST)
...@@ -136,6 +139,8 @@ class AsmFunctionType : public AsmCallableType { ...@@ -136,6 +139,8 @@ class AsmFunctionType : public AsmCallableType {
AsmType* ValidateCall(AsmType* return_type, AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override; const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
protected: protected:
AsmFunctionType(Zone* zone, AsmType* return_type) AsmFunctionType(Zone* zone, AsmType* return_type)
...@@ -168,6 +173,8 @@ class AsmOverloadedFunctionType final : public AsmCallableType { ...@@ -168,6 +173,8 @@ class AsmOverloadedFunctionType final : public AsmCallableType {
std::string Name() override; std::string Name() override;
AsmType* ValidateCall(AsmType* return_type, AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override; const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
ZoneVector<AsmType*> overloads_; ZoneVector<AsmType*> overloads_;
...@@ -181,6 +188,8 @@ class AsmFFIType final : public AsmCallableType { ...@@ -181,6 +188,8 @@ class AsmFFIType final : public AsmCallableType {
std::string Name() override { return "Function"; } std::string Name() override { return "Function"; }
AsmType* ValidateCall(AsmType* return_type, AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override; const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
private: private:
friend AsmType; friend AsmType;
...@@ -198,6 +207,8 @@ class AsmFunctionTableType : public AsmCallableType { ...@@ -198,6 +207,8 @@ class AsmFunctionTableType : public AsmCallableType {
AsmType* ValidateCall(AsmType* return_type, AsmType* ValidateCall(AsmType* return_type,
const ZoneVector<AsmType*>& args) override; const ZoneVector<AsmType*>& args) override;
bool CanBeInvokedWith(AsmType* return_type,
const ZoneVector<AsmType*>& args) override;
size_t length() const { return length_; } size_t length() const { return length_; }
AsmType* signature() { return signature_; } AsmType* signature() { return signature_; }
......
This diff is collapsed.
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#define V8_ASMJS_ASM_WASM_BUILDER_H_ #define V8_ASMJS_ASM_WASM_BUILDER_H_
#include "src/allocation.h" #include "src/allocation.h"
#include "src/asmjs/typing-asm.h" #include "src/asmjs/asm-typer.h"
#include "src/objects.h" #include "src/objects.h"
#include "src/wasm/encoder.h" #include "src/wasm/encoder.h"
#include "src/zone.h" #include "src/zone.h"
......
...@@ -504,7 +504,6 @@ DEFINE_BOOL(wasm_loop_assignment_analysis, true, ...@@ -504,7 +504,6 @@ DEFINE_BOOL(wasm_loop_assignment_analysis, true,
"perform loop assignment analysis for WASM") "perform loop assignment analysis for WASM")
DEFINE_BOOL(validate_asm, false, "validate asm.js modules before compiling") DEFINE_BOOL(validate_asm, false, "validate asm.js modules before compiling")
DEFINE_BOOL(enable_simd_asmjs, false, "enable SIMD.js in asm.js stdlib")
DEFINE_BOOL(dump_wasm_module, false, "dump WASM module bytes") DEFINE_BOOL(dump_wasm_module, false, "dump WASM module bytes")
DEFINE_STRING(dump_wasm_module_path, NULL, "directory to dump wasm modules to") DEFINE_STRING(dump_wasm_module_path, NULL, "directory to dump wasm modules to")
......
...@@ -149,11 +149,8 @@ v8::internal::wasm::ZoneBuffer* TranslateAsmModule( ...@@ -149,11 +149,8 @@ v8::internal::wasm::ZoneBuffer* TranslateAsmModule(
info->set_literal( info->set_literal(
info->scope()->declarations()->at(0)->AsFunctionDeclaration()->fun()); info->scope()->declarations()->at(0)->AsFunctionDeclaration()->fun());
v8::internal::AsmTyper typer(info->isolate(), info->zone(), *(info->script()), v8::internal::wasm::AsmTyper typer(info->isolate(), info->zone(),
info->literal()); *(info->script()), info->literal());
if (i::FLAG_enable_simd_asmjs) {
typer.set_allow_simd(true);
}
if (!typer.Validate()) { if (!typer.Validate()) {
thrower->Error("Asm.js validation failed: %s", typer.error_message()); thrower->Error("Asm.js validation failed: %s", typer.error_message());
return nullptr; return nullptr;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// Flags: --expose-wasm // Flags: --expose-wasm
function __f_61(stdlib, buffer) { function __f_61(stdlib, foreign, buffer) {
"use asm"; "use asm";
var __v_14 = new stdlib.Float64Array(buffer); var __v_14 = new stdlib.Float64Array(buffer);
function __f_74() { function __f_74() {
......
...@@ -83,33 +83,34 @@ ...@@ -83,33 +83,34 @@
} }
function caller() { function caller() {
if (!deltaEqual(StdlibMathSqrt(123.0), 11.090536506409418)) return 0; if (!(deltaEqual(+StdlibMathSqrt(123.0), 11.090536506409418)|0)) return 0;
if (StdlibMathSqrt(fround(256.0)) != fround(16.0)) return 0; if (fround(StdlibMathSqrt(fround(256.0))) != fround(16.0)) return 0;
if (StdlibMathCeil(123.7) != 124.0) return 0; if (+StdlibMathCeil(123.7) != 124.0) return 0;
if (StdlibMathCeil(fround(123.7)) != fround(124.0)) return 0; if (fround(StdlibMathCeil(fround(123.7))) != fround(124.0)) return 0;
if (StdlibMathFloor(123.7) != 123.0) return 0; if (+StdlibMathFloor(123.7) != 123.0) return 0;
if (StdlibMathFloor(fround(123.7)) != fround(123.0)) return 0; if (fround(StdlibMathFloor(fround(123.7))) != fround(123.0)) return 0;
if (StdlibMathAbs(-123.0) != 123.0) return 0; if (+StdlibMathAbs(-123.0) != 123.0) return 0;
if (StdlibMathAbs(fround(-123.0)) != fround(123.0)) return 0; if (fround(StdlibMathAbs(fround(-123.0))) != fround(123.0)) return 0;
if (StdlibMathMin(123.4, 1236.4) != 123.4) return 0; if (+StdlibMathMin(123.4, 1236.4) != 123.4) return 0;
if (StdlibMathMin(fround(123.4), if (fround(StdlibMathMin(fround(123.4),
fround(1236.4)) != fround(123.4)) return 0; fround(1236.4))) != fround(123.4)) return 0;
if (StdlibMathMax(123.4, 1236.4) != 1236.4) return 0; if (+StdlibMathMax(123.4, 1236.4) != 1236.4) return 0;
if (StdlibMathMax(fround(123.4), fround(1236.4)) if (fround(StdlibMathMax(fround(123.4), fround(1236.4)))
!= fround(1236.4)) return 0; != fround(1236.4)) return 0;
if (!deltaEqual(StdlibMathAcos(0.1), 1.4706289056333368)) return 0; if (!(deltaEqual(+StdlibMathAcos(0.1), 1.4706289056333368)|0)) return 0;
if (!deltaEqual(StdlibMathAsin(0.2), 0.2013579207903308)) return 0; if (!(deltaEqual(+StdlibMathAsin(0.2), 0.2013579207903308)|0)) return 0;
if (!deltaEqual(StdlibMathAtan(0.2), 0.19739555984988078)) return 0; if (!(deltaEqual(+StdlibMathAtan(0.2), 0.19739555984988078)|0)) return 0;
if (!deltaEqual(StdlibMathCos(0.2), 0.9800665778412416)) return 0; if (!(deltaEqual(+StdlibMathCos(0.2), 0.9800665778412416)|0)) return 0;
if (!deltaEqual(StdlibMathSin(0.2), 0.19866933079506122)) return 0; if (!(deltaEqual(+StdlibMathSin(0.2), 0.19866933079506122)|0)) return 0;
if (!deltaEqual(StdlibMathTan(0.2), 0.20271003550867250)) return 0; if (!(deltaEqual(+StdlibMathTan(0.2), 0.20271003550867250)|0)) return 0;
if (!deltaEqual(StdlibMathExp(0.2), 1.2214027581601699)) return 0; if (!(deltaEqual(+StdlibMathExp(0.2), 1.2214027581601699)|0)) return 0;
if (!deltaEqual(StdlibMathLog(0.2), -1.6094379124341003)) return 0; if (!(deltaEqual(+StdlibMathLog(0.2), -1.6094379124341003)|0)) return 0;
if (StdlibMathImul(6, 7) != 42) return 0; if ((StdlibMathImul(6, 7)|0) != 42) return 0;
if (!deltaEqual(StdlibMathAtan2(6.0, 7.0), 0.7086262721276703)) return 0; if (!(deltaEqual(+StdlibMathAtan2(6.0, 7.0), 0.7086262721276703)|0))
if (StdlibMathPow(6.0, 7.0) != 279936.0) return 0; return 0;
if (+StdlibMathPow(6.0, 7.0) != 279936.0) return 0;
return 1; return 1;
} }
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
function caller() { function caller() {
var ret = 0; var ret = 0;
var x = 7; var x = 7;
switch (x) { switch (x|0) {
case 1: { case 1: {
return 0; return 0;
} }
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
function caller() { function caller() {
var ret = 0; var ret = 0;
var x = 7; var x = 7;
switch (x) { switch (x|0) {
case 1: return 0; case 1: return 0;
case 7: { case 7: {
ret = 12; ret = 12;
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
} }
default: return 0; default: return 0;
} }
switch (x) { switch (x|0) {
case 1: return 0; case 1: return 0;
case 8: return 0; case 8: return 0;
default: ret = (ret + 11)|0; default: ret = (ret + 11)|0;
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
function caller() { function caller() {
var x = 17; var x = 17;
var ret = 0; var ret = 0;
switch (x) { switch (x|0) {
case 17: case 17:
case 14: ret = 39; case 14: ret = 39;
case 1: ret = (ret + 3)|0; case 1: ret = (ret + 3)|0;
...@@ -89,10 +89,10 @@ ...@@ -89,10 +89,10 @@
function caller() { function caller() {
var x = 3; var x = 3;
var y = -13; var y = -13;
switch (x) { switch (x|0) {
case 1: return 0; case 1: return 0;
case 3: { case 3: {
switch (y) { switch (y|0) {
case 2: return 0; case 2: return 0;
case -13: return 43; case -13: return 43;
default: return 0; default: return 0;
......
...@@ -43,9 +43,11 @@ function IntTest() { ...@@ -43,9 +43,11 @@ function IntTest() {
function sum(a, b) { function sum(a, b) {
a = a|0; a = a|0;
b = b|0; b = b|0;
var c = (b + 1)|0 var c = 0;
var d = 3.0; var d = 3.0;
var e = ~~d; // double conversion var e = 0;
e = ~~d; // double conversion
c = (b + 1)|0
return (a + c + 1)|0; return (a + c + 1)|0;
} }
...@@ -68,8 +70,9 @@ function Float64Test() { ...@@ -68,8 +70,9 @@ function Float64Test() {
} }
function caller() { function caller() {
var a = +sum(70.1,10.2); var a = 0.0;
var ret = 0|0; var ret = 0|0;
a = +sum(70.1,10.2);
if (a == 80.3) { if (a == 80.3) {
ret = 1|0; ret = 1|0;
} else { } else {
...@@ -89,7 +92,8 @@ function BadModule() { ...@@ -89,7 +92,8 @@ function BadModule() {
function caller(a, b) { function caller(a, b) {
a = a|0; a = a|0;
b = b+0; b = b+0;
var c = (b + 1)|0 var c = 0;
c = (b + 1)|0
return (a + c + 1)|0; return (a + c + 1)|0;
} }
...@@ -293,12 +297,12 @@ function TestBreakInNestedWhile() { ...@@ -293,12 +297,12 @@ function TestBreakInNestedWhile() {
function caller() { function caller() {
var x = 1.0; var x = 1.0;
var ret = 0;
while(x < 1.5) { while(x < 1.5) {
while(1) while(1)
break; break;
x = +(x + 0.25); x = +(x + 0.25);
} }
var ret = 0;
if (x == 1.5) { if (x == 1.5) {
ret = 9; ret = 9;
} }
...@@ -405,7 +409,8 @@ function TestNot() { ...@@ -405,7 +409,8 @@ function TestNot() {
"use asm"; "use asm";
function caller() { function caller() {
var a = !(2 > 3); var a = 0;
a = !(2 > 3);
return a | 0; return a | 0;
} }
...@@ -886,7 +891,9 @@ function TestFunctionTableSingleFunction() { ...@@ -886,7 +891,9 @@ function TestFunctionTableSingleFunction() {
} }
function caller() { function caller() {
return function_table[0&0]() | 0; // TODO(jpp): the parser optimizes function_table[0&0] to function table[0].
var v = 0;
return function_table[v&0]() | 0;
} }
var function_table = [dummy] var function_table = [dummy]
...@@ -911,8 +918,9 @@ function TestFunctionTableMultipleFunctions() { ...@@ -911,8 +918,9 @@ function TestFunctionTableMultipleFunctions() {
} }
function caller() { function caller() {
if ((function_table[0&1](50)|0) == 51) { var i = 0, j = 1;
if ((function_table[1&1](60)|0) == 62) { if ((function_table[i&1](50)|0) == 51) {
if ((function_table[j&1](60)|0) == 62) {
return 73; return 73;
} }
} }
...@@ -1350,7 +1358,7 @@ assertWasm(1, TestXor); ...@@ -1350,7 +1358,7 @@ assertWasm(1, TestXor);
"use asm"; "use asm";
function func() { function func() {
var a = 1; var a = 1;
return ((a * 3) + (4 * a)) | 0; return (((a * 3)|0) + ((4 * a)|0)) | 0;
} }
return {func: func}; return {func: func};
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment