| // Copyright 2015 The Emscripten Authors. All rights reserved. |
| // Emscripten is available under two separate licenses, the MIT license and the |
| // University of Illinois/NCSA Open Source License. Both these licenses can be |
| // found in the LICENSE file. |
| |
| #include "optimizer.h" |
| |
| using namespace cashew; |
| |
| IString ASM_FLOAT_ZERO; |
| |
| IString SIMD_INT8X16_CHECK("SIMD_Int8x16_check"), |
| SIMD_INT16X8_CHECK("SIMD_Int16x8_check"), |
| SIMD_INT32X4_CHECK("SIMD_Int32x4_check"), |
| SIMD_FLOAT32X4_CHECK("SIMD_Float32x4_check"), |
| SIMD_FLOAT64X2_CHECK("SIMD_Float64x2_check"), |
| SIMD_BOOL8X16_CHECK("SIMD_Bool8x16_check"), |
| SIMD_BOOL16X8_CHECK("SIMD_Bool16x8_check"), |
| SIMD_BOOL32X4_CHECK("SIMD_Bool32x4_check"), |
| SIMD_BOOL64X2_CHECK("SIMD_Bool64x2_check"); |
| |
| bool isInteger(double x) { |
| return fmod(x, 1) == 0; |
| } |
| |
| bool isInteger32(double x) { |
| return isInteger(x) && (x == (int32_t)x || x == (uint32_t)x); |
| } |
| |
| int parseInt(const char *str) { |
| int ret = *str - '0'; |
| while (*(++str)) { |
| ret *= 10; |
| ret += *str - '0'; |
| } |
| return ret; |
| } |
| |
| HeapInfo parseHeap(const char *name) { |
| HeapInfo ret; |
| if (name[0] != 'H' || name[1] != 'E' || name[2] != 'A' || name[3] != 'P') { |
| ret.valid = false; |
| return ret; |
| } |
| ret.valid = true; |
| ret.unsign = name[4] == 'U'; |
| ret.floaty = name[4] == 'F'; |
| ret.bits = parseInt(name + (ret.unsign || ret.floaty ? 5 : 4)); |
| ret.type = !ret.floaty ? ASM_INT : (ret.bits == 64 ? ASM_DOUBLE : ASM_FLOAT); |
| return ret; |
| } |
| |
| AsmType detectType(Ref node, AsmData *asmData, bool inVarDef) { |
| switch (node[0]->getCString()[0]) { |
| case 'n': { |
| if (node[0] == NUM) { |
| if (!isInteger(node[1]->getNumber())) return ASM_DOUBLE; |
| return ASM_INT; |
| } else if (node[0] == NAME) { |
| if (asmData) { |
| AsmType ret = asmData->getType(node[1]->getCString()); |
| if (ret != ASM_NONE) return ret; |
| } |
| if (!inVarDef) { |
| if (!ASM_FLOAT_ZERO.isNull() && node[1] == ASM_FLOAT_ZERO) return ASM_FLOAT; |
| if (node[1] == INF || node[1] == NaN) return ASM_DOUBLE; |
| if (node[1] == TEMP_RET0) return ASM_INT; |
| return ASM_NONE; |
| } |
| // We are in a variable definition, where Math_fround(0) optimized into a global constant becomes f0 = Math_fround(0) |
| if (ASM_FLOAT_ZERO.isNull()) ASM_FLOAT_ZERO = node[1]->getIString(); |
| else assert(node[1] == ASM_FLOAT_ZERO); |
| return ASM_FLOAT; |
| } |
| break; |
| } |
| case 'u': { |
| if (node[0] == UNARY_PREFIX) { |
| switch (node[1]->getCString()[0]) { |
| case '+': return ASM_DOUBLE; |
| case '-': return detectType(node[2], asmData, inVarDef); |
| case '!': case '~': return ASM_INT; |
| } |
| break; |
| } |
| break; |
| } |
| case 'c': { |
| if (node[0] == CALL) { |
| if (node[1][0] == NAME) { |
| IString name = node[1][1]->getIString(); |
| if (name == MATH_FROUND) 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; |
| else if (name == SIMD_INT16X8 || name == SIMD_INT16X8_CHECK) return ASM_INT16X8; |
| else if (name == SIMD_INT32X4 || name == SIMD_INT32X4_CHECK) return ASM_INT32X4; |
| else if (name == SIMD_BOOL8X16 || name == SIMD_BOOL8X16_CHECK) return ASM_BOOL8X16; |
| else if (name == SIMD_BOOL16X8 || name == SIMD_BOOL16X8_CHECK) return ASM_BOOL16X8; |
| else if (name == SIMD_BOOL32X4 || name == SIMD_BOOL32X4_CHECK) return ASM_BOOL32X4; |
| else if (name == SIMD_BOOL64X2 || name == SIMD_BOOL64X2_CHECK) return ASM_BOOL64X2; |
| } |
| return ASM_NONE; |
| } else if (node[0] == CONDITIONAL) { |
| return detectType(node[2], asmData, inVarDef); |
| } |
| break; |
| } |
| case 'b': { |
| if (node[0] == BINARY) { |
| switch (node[1]->getCString()[0]) { |
| case '+': case '-': |
| case '*': case '/': case '%': return detectType(node[2], asmData, inVarDef); |
| case '|': case '&': case '^': case '<': case '>': // handles <<, >>, >>=, <=, >= |
| case '=': case '!': { // handles ==, != |
| return ASM_INT; |
| } |
| } |
| } |
| break; |
| } |
| case 's': { |
| if (node[0] == SEQ) { |
| return detectType(node[2], asmData, inVarDef); |
| } else if (node[0] == SUB) { |
| assert(node[1][0] == NAME); |
| HeapInfo info = parseHeap(node[1][1]->getCString()); |
| if (info.valid) return ASM_NONE; |
| return info.floaty ? ASM_DOUBLE : ASM_INT; // XXX ASM_FLOAT? |
| } |
| break; |
| } |
| } |
| //dump("horrible", node); |
| //assert(0); |
| return ASM_NONE; |
| } |
| |
| AsmSign detectSign(Ref node) { |
| 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(); |
| } |
| } 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(); |
| } |
| } 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]); |
| } else if (type == CALL) { |
| if (node[1][0] == NAME && node[1][1] == MATH_FROUND) return ASM_NONSIGNED; |
| } |
| abort(); |
| } |
| |