diff options
Diffstat (limited to 'src/emscripten-optimizer')
-rw-r--r-- | src/emscripten-optimizer/optimizer-shared.cpp | 122 | ||||
-rw-r--r-- | src/emscripten-optimizer/optimizer.h | 23 | ||||
-rw-r--r-- | src/emscripten-optimizer/parser.h | 10 | ||||
-rw-r--r-- | src/emscripten-optimizer/simple_ast.cpp | 2 | ||||
-rw-r--r-- | src/emscripten-optimizer/simple_ast.h | 139 |
5 files changed, 279 insertions, 17 deletions
diff --git a/src/emscripten-optimizer/optimizer-shared.cpp b/src/emscripten-optimizer/optimizer-shared.cpp index 1c36efc62..7fe91eeab 100644 --- a/src/emscripten-optimizer/optimizer-shared.cpp +++ b/src/emscripten-optimizer/optimizer-shared.cpp @@ -19,6 +19,12 @@ bool isInteger32(double x) { return isInteger(x) && (x == (int32_t)x || x == (uint32_t)x); } +int32_t toInteger32(double x) { + if (x == (int32_t)x) return (int32_t)x; + assert(x == (uint32_t)x); + return (uint32_t)x; +} + int parseInt(const char *str) { int ret = *str - '0'; while (*(++str)) { @@ -42,7 +48,7 @@ HeapInfo parseHeap(const char *name) { return ret; } -AsmType detectType(Ref node, AsmData *asmData, bool inVarDef) { +AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFround) { switch (node[0]->getCString()[0]) { case 'n': { if (node[0] == NUM) { @@ -69,7 +75,7 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef) { if (node[0] == UNARY_PREFIX) { switch (node[1]->getCString()[0]) { case '+': return ASM_DOUBLE; - case '-': return detectType(node[2], asmData, inVarDef); + case '-': return detectType(node[2], asmData, inVarDef, minifiedFround); case '!': case '~': return ASM_INT; } break; @@ -80,7 +86,7 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef) { if (node[0] == CALL) { if (node[1][0] == NAME) { IString name = node[1][1]->getIString(); - if (name == MATH_FROUND) return ASM_FLOAT; + if (name == MATH_FROUND || name == minifiedFround) return ASM_FLOAT; else if (name == SIMD_FLOAT32X4 || name == SIMD_FLOAT32X4_CHECK) return ASM_FLOAT32X4; else if (name == SIMD_FLOAT64X2 || name == SIMD_FLOAT64X2_CHECK) return ASM_FLOAT64X2; else if (name == SIMD_INT8X16 || name == SIMD_INT8X16_CHECK) return ASM_INT8X16; @@ -89,7 +95,7 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef) { } return ASM_NONE; } else if (node[0] == CONDITIONAL) { - return detectType(node[2], asmData, inVarDef); + return detectType(node[2], asmData, inVarDef, minifiedFround); } break; } @@ -97,7 +103,7 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef) { if (node[0] == BINARY) { switch (node[1]->getCString()[0]) { case '+': case '-': - case '*': case '/': case '%': return detectType(node[2], asmData, inVarDef); + case '*': case '/': case '%': return detectType(node[2], asmData, inVarDef, minifiedFround); case '|': case '&': case '^': case '<': case '>': // handles <<, >>, >>=, <=, >= case '=': case '!': { // handles ==, != return ASM_INT; @@ -108,7 +114,7 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef) { } case 's': { if (node[0] == SEQ) { - return detectType(node[2], asmData, inVarDef); + return detectType(node[2], asmData, inVarDef, minifiedFround); } else if (node[0] == SUB) { assert(node[1][0] == NAME); HeapInfo info = parseHeap(node[1][1]->getCString()); @@ -123,3 +129,107 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef) { return ASM_NONE; } +static void abort_on(Ref node) { + node->stringify(std::cerr); + std::cerr << '\n'; + abort(); +} + +AsmSign detectSign(Ref node, IString minifiedFround) { + IString type = node[0]->getIString(); + if (type == BINARY) { + IString op = node[1]->getIString(); + switch (op.str[0]) { + case '>': { + if (op == TRSHIFT) return ASM_UNSIGNED; + // fallthrough + } + case '|': case '&': case '^': case '<': case '=': case '!': return ASM_SIGNED; + case '+': case '-': return ASM_FLEXIBLE; + case '*': case '/': return ASM_NONSIGNED; // without a coercion, these are double + default: abort_on(node); + } + } else if (type == UNARY_PREFIX) { + IString op = node[1]->getIString(); + switch (op.str[0]) { + case '-': return ASM_FLEXIBLE; + case '+': return ASM_NONSIGNED; // XXX double + case '~': return ASM_SIGNED; + default: abort_on(node); + } + } else if (type == NUM) { + double value = node[1]->getNumber(); + if (value < 0) return ASM_SIGNED; + if (value > uint32_t(-1) || fmod(value, 1) != 0) return ASM_NONSIGNED; + if (value == int32_t(value)) return ASM_FLEXIBLE; + return ASM_UNSIGNED; + } else if (type == NAME) { + return ASM_FLEXIBLE; + } else if (type == CONDITIONAL) { + return detectSign(node[2], minifiedFround); + } else if (type == CALL) { + if (node[1][0] == NAME && (node[1][1] == MATH_FROUND || node[1][1] == minifiedFround)) return ASM_NONSIGNED; + } else if (type == SEQ) { + return detectSign(node[2], minifiedFround); + } + abort_on(node); + abort(); // avoid warning +} + +Ref makeAsmCoercedZero(AsmType type) { + switch (type) { + case ASM_INT: return ValueBuilder::makeNum(0); break; + case ASM_DOUBLE: return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeNum(0)); break; + case ASM_FLOAT: { + if (!ASM_FLOAT_ZERO.isNull()) { + return ValueBuilder::makeName(ASM_FLOAT_ZERO); + } else { + return ValueBuilder::makeCall(MATH_FROUND, ValueBuilder::makeNum(0)); + } + break; + } + case ASM_FLOAT32X4: { + return ValueBuilder::makeCall(SIMD_FLOAT32X4, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + break; + } + case ASM_FLOAT64X2: { + return ValueBuilder::makeCall(SIMD_FLOAT64X2, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + break; + } + case ASM_INT8X16: { + return ValueBuilder::makeCall(SIMD_INT8X16, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + break; + } + case ASM_INT16X8: { + return ValueBuilder::makeCall(SIMD_INT16X8, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + break; + } + case ASM_INT32X4: { + return ValueBuilder::makeCall(SIMD_INT32X4, ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0), ValueBuilder::makeNum(0)); + break; + } + default: assert(0); + } + abort(); +} + +Ref makeAsmCoercion(Ref node, AsmType type) { + switch (type) { + case ASM_INT: return ValueBuilder::makeBinary(node, OR, ValueBuilder::makeNum(0)); + case ASM_DOUBLE: return ValueBuilder::makeUnary(PLUS, node); + case ASM_FLOAT: return ValueBuilder::makeCall(MATH_FROUND, node); + case ASM_FLOAT32X4: return ValueBuilder::makeCall(SIMD_FLOAT32X4_CHECK, node); + case ASM_FLOAT64X2: return ValueBuilder::makeCall(SIMD_FLOAT64X2_CHECK, node); + case ASM_INT8X16: return ValueBuilder::makeCall(SIMD_INT8X16_CHECK, node); + case ASM_INT16X8: return ValueBuilder::makeCall(SIMD_INT16X8_CHECK, node); + case ASM_INT32X4: return ValueBuilder::makeCall(SIMD_INT32X4_CHECK, node); + case ASM_NONE: + default: return node; // non-validating code, emit nothing XXX this is dangerous, we should only allow this when we know we are not validating + } +} + +Ref makeSigning(Ref node, AsmSign sign) { + assert(sign == ASM_SIGNED || sign == ASM_UNSIGNED); + return ValueBuilder::makeBinary(node, sign == ASM_SIGNED ? OR : TRSHIFT, ValueBuilder::makeNum(0)); +} + diff --git a/src/emscripten-optimizer/optimizer.h b/src/emscripten-optimizer/optimizer.h index 5edcaad87..6850214c2 100644 --- a/src/emscripten-optimizer/optimizer.h +++ b/src/emscripten-optimizer/optimizer.h @@ -39,7 +39,7 @@ enum AsmType { struct AsmData; -AsmType detectType(cashew::Ref node, AsmData *asmData=nullptr, bool inVarDef=false); +AsmType detectType(cashew::Ref node, AsmData *asmData=nullptr, bool inVarDef=false, cashew::IString minifiedFround=cashew::IString()); struct AsmData { struct Local { @@ -102,8 +102,8 @@ struct AsmData { }; bool isInteger(double x); - bool isInteger32(double x); +int32_t toInteger32(double x); extern cashew::IString ASM_FLOAT_ZERO; @@ -123,5 +123,24 @@ struct HeapInfo { HeapInfo parseHeap(const char *name); +enum AsmSign { + ASM_FLEXIBLE = 0, // small constants can be signed or unsigned, variables are also flexible + ASM_SIGNED = 1, + ASM_UNSIGNED = 2, + ASM_NONSIGNED = 3, +}; + +extern AsmSign detectSign(cashew::Ref node, cashew::IString minifiedFround); + +inline cashew::Ref deStat(cashew::Ref node) { + if (node[0] == cashew::STAT) return node[1]; + return node; +} + +cashew::Ref makeAsmCoercedZero(AsmType type); +cashew::Ref makeAsmCoercion(cashew::Ref node, AsmType type); + +cashew::Ref makeSigning(cashew::Ref node, AsmSign sign); + #endif // __optimizer_h__ diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h index 8ca07c6ed..386195d77 100644 --- a/src/emscripten-optimizer/parser.h +++ b/src/emscripten-optimizer/parser.h @@ -830,6 +830,16 @@ class Parser { src++; return Builder::makeBlock(); // we don't need the brackets here, but oh well } + if (*src == '{') { // detect a trivial {} in a statement context + char *before = src; + src++; + skipSpace(src); + if (*src == '}') { + src++; + return Builder::makeBlock(); // we don't need the brackets here, but oh well + } + src = before; + } NodeRef ret = parseElement(src, seps); skipSpace(src); if (*src == ';') { diff --git a/src/emscripten-optimizer/simple_ast.cpp b/src/emscripten-optimizer/simple_ast.cpp index 89b8ce8ed..dcecab33d 100644 --- a/src/emscripten-optimizer/simple_ast.cpp +++ b/src/emscripten-optimizer/simple_ast.cpp @@ -253,7 +253,7 @@ void traverseFunctions(Ref ast, std::function<void (Ref)> visit) { // ValueBuilder -IStringSet ValueBuilder::statable("assign call binary unary-prefix if name num conditional dot new sub seq string object array"); +IStringSet ValueBuilder::statable("assign call binary unary-prefix name num conditional dot new sub seq string object array"); } // namespace cashew diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h index 4012ff8f6..5952eee72 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -1268,9 +1268,20 @@ struct JSPrinter { pretty ? emit(", ") : emit(','); newline(); } - emit('"'); - emit(args[i][0]->getCString()); - emit("\":"); + const char *str = args[i][0]->getCString(); + const char *check = str; + bool needQuote = false; + while (*check) { + if (!isalnum(*check) && *check != '_' && *check != '$') { + needQuote = true; + break; + } + check++; + } + if (needQuote) emit('"'); + emit(str); + if (needQuote) emit('"'); + emit(":"); space(); print(args[i][1]); } @@ -1289,15 +1300,15 @@ class ValueBuilder { return &arena.alloc()->setString(s); } - static Ref makeRawArray(int size_hint=0) { - return &arena.alloc()->setArray(size_hint); - } - static Ref makeNull() { return &arena.alloc()->setNull(); } public: + static Ref makeRawArray(int size_hint=0) { + return &arena.alloc()->setArray(size_hint); + } + static Ref makeToplevel() { return &makeRawArray(2)->push_back(makeRawString(TOPLEVEL)) .push_back(makeRawArray()); @@ -1336,6 +1347,80 @@ public: .push_back(target) .push_back(makeRawArray()); } + static Ref makeCall(Ref target, Ref arg) { + Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) + .push_back(target) + .push_back(makeRawArray()); + ret[2]->push_back(arg); + return ret; + } + static Ref makeCall(IString target) { + Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) + .push_back(makeName(target)) + .push_back(makeRawArray()); + return ret; + } + static Ref makeCall(IString target, Ref arg) { + Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) + .push_back(makeName(target)) + .push_back(makeRawArray(1)); + ret[2]->push_back(arg); + return ret; + } + static Ref makeCall(IString target, Ref arg1, Ref arg2) { + Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) + .push_back(makeName(target)) + .push_back(makeRawArray(2)); + ret[2]->push_back(arg1); + ret[2]->push_back(arg2); + return ret; + } + static Ref makeCall(IString target, Ref arg1, Ref arg2, Ref arg3, Ref arg4) { + Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) + .push_back(makeName(target)) + .push_back(makeRawArray(4)); + ret[2]->push_back(arg1); + ret[2]->push_back(arg2); + ret[2]->push_back(arg3); + ret[2]->push_back(arg4); + return ret; + } + static Ref makeCall(IString target, Ref arg1, Ref arg2, Ref arg3, Ref arg4, Ref arg5, Ref arg6, Ref arg7, Ref arg8) { + Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) + .push_back(makeName(target)) + .push_back(makeRawArray(8)); + ret[2]->push_back(arg1); + ret[2]->push_back(arg2); + ret[2]->push_back(arg3); + ret[2]->push_back(arg4); + ret[2]->push_back(arg5); + ret[2]->push_back(arg6); + ret[2]->push_back(arg7); + ret[2]->push_back(arg8); + return ret; + } + static Ref makeCall(IString target, Ref arg1, Ref arg2, Ref arg3, Ref arg4, Ref arg5, Ref arg6, Ref arg7, Ref arg8, Ref arg9, Ref arg10, Ref arg11, Ref arg12, Ref arg13, Ref arg14, Ref arg15, Ref arg16) { + Ref ret = &makeRawArray(3)->push_back(makeRawString(CALL)) + .push_back(makeName(target)) + .push_back(makeRawArray(16)); + ret[2]->push_back(arg1); + ret[2]->push_back(arg2); + ret[2]->push_back(arg3); + ret[2]->push_back(arg4); + ret[2]->push_back(arg5); + ret[2]->push_back(arg6); + ret[2]->push_back(arg7); + ret[2]->push_back(arg8); + ret[2]->push_back(arg9); + ret[2]->push_back(arg10); + ret[2]->push_back(arg11); + ret[2]->push_back(arg12); + ret[2]->push_back(arg13); + ret[2]->push_back(arg14); + ret[2]->push_back(arg15); + ret[2]->push_back(arg16); + return ret; + } static void appendToCall(Ref call, Ref element) { assert(call[0] == CALL); @@ -1358,6 +1443,15 @@ public: static Ref makeInt(uint32_t num) { return makeDouble(double(num)); } + static Ref makeNum(double num) { + return makeDouble(num); + } + + static Ref makeUnary(IString op, Ref value) { + return &makeRawArray(3)->push_back(makeRawString(UNARY_PREFIX)) + .push_back(makeRawString(op)) + .push_back(value); + } static Ref makeBinary(Ref left, IString op, Ref right) { if (op == SET) { @@ -1395,7 +1489,7 @@ public: func[2]->push_back(makeRawString(arg)); } - static Ref makeVar(bool is_const) { + static Ref makeVar(bool is_const=false) { return &makeRawArray(2)->push_back(makeRawString(VAR)) .push_back(makeRawArray()); } @@ -1432,6 +1526,12 @@ public: .push_back(ifFalse); } + static Ref makeSeq(Ref left, Ref right) { + return &makeRawArray(3)->push_back(makeRawString(SEQ)) + .push_back(left) + .push_back(right); + } + static Ref makeDo(Ref body, Ref condition) { return &makeRawArray(3)->push_back(makeRawString(DO)) .push_back(condition) @@ -1526,6 +1626,29 @@ public: array[1]->push_back(&makeRawArray(2)->push_back(makeRawString(key)) .push_back(value)); } + + static Ref makeAssign(Ref target, Ref value) { + return &makeRawArray(3)->push_back(makeRawString(ASSIGN)) + .push_back(&arena.alloc()->setBool(true)) + .push_back(target) + .push_back(value); + } + static Ref makeAssign(IString target, Ref value) { + return &makeRawArray(3)->push_back(makeRawString(ASSIGN)) + .push_back(&arena.alloc()->setBool(true)) + .push_back(makeName(target)) + .push_back(value); + } + + static Ref makeSub(Ref obj, Ref index) { + return &makeRawArray(2)->push_back(makeRawString(SUB)) + .push_back(obj) + .push_back(index); + } + + static Ref makePtrShift(Ref ptr, int shifts) { + return makeBinary(ptr, RSHIFT, makeInt(shifts)); + } }; // Tolerates 0.0 in the input; does not trust a +() to be there. |