From 818fa1dc19b0edcaaf1a8aa78b94bf052420252d Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Mon, 30 Jan 2017 12:13:56 -0800 Subject: refactor asm.js ast to use a number node directly instead of [NUM, number] --- src/emscripten-optimizer/parser.h | 1 - 1 file changed, 1 deletion(-) (limited to 'src/emscripten-optimizer/parser.h') diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h index 060b71272..e43709f9b 100644 --- a/src/emscripten-optimizer/parser.h +++ b/src/emscripten-optimizer/parser.h @@ -54,7 +54,6 @@ extern IString TOPLEVEL, SEQ, SUB, CALL, - NUM, LABEL, BREAK, CONTINUE, -- cgit v1.2.3 From abb48f7460b26e14076fd34ed1f7274ab3a949cc Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 31 Jan 2017 10:43:19 -0800 Subject: refactor asm.js ast to use a string node directly instead of [NAME, string] --- src/asm2wasm.h | 147 +++++++++++++------------- src/emscripten-optimizer/optimizer-shared.cpp | 46 ++++---- src/emscripten-optimizer/parser.cpp | 1 - src/emscripten-optimizer/parser.h | 1 - src/emscripten-optimizer/simple_ast.cpp | 2 +- src/emscripten-optimizer/simple_ast.h | 18 ++-- 6 files changed, 107 insertions(+), 108 deletions(-) (limited to 'src/emscripten-optimizer/parser.h') diff --git a/src/asm2wasm.h b/src/asm2wasm.h index bc65e87fc..13eb393ff 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -299,8 +299,8 @@ private: std::map> importedFunctionTypes; void noteImportedFunctionCall(Ref ast, WasmType resultType, CallImport* call) { - assert(ast[0] == CALL && ast[1][0] == NAME); - IString importName = ast[1][1]->getIString(); + assert(ast[0] == CALL && ast[1]->isString()); + IString importName = ast[1]->getIString(); auto type = make_unique(); type->name = IString((std::string("type$") + importName.str).c_str(), false); // TODO: make a list of such types type->result = resultType; @@ -358,16 +358,16 @@ public: private: AsmType detectAsmType(Ref ast, AsmData *data) { - if (ast->isArray(NAME)) { - IString name = ast[1]->getIString(); + if (ast->isString()) { + IString name = ast->getIString(); if (!data->isLocal(name)) { // must be global assert(mappedGlobals.find(name) != mappedGlobals.end()); return wasmToAsmType(mappedGlobals[name].type); } - } else if (ast->isArray(SUB) && ast[1][0] == NAME) { + } else if (ast->isArray(SUB) && ast[1]->isString()) { // could be a heap access, use view info - auto view = views.find(ast[1][1]->getIString()); + auto view = views.find(ast[1]->getIString()); if (view != views.end()) { return view->second.type; } @@ -462,7 +462,7 @@ private: } else { return Literal(ast->getNumber()); } - } else if (ast[0] == UNARY_PREFIX) { + } else if (ast->isArray(UNARY_PREFIX)) { if (ast[1] == PLUS && ast[2]->isNumber()) { return Literal((double)ast[2]->getNumber()); } @@ -472,13 +472,13 @@ private: if (isUInteger32(num)) return Literal((uint32_t)num); assert(false && "expected signed or unsigned int32"); } - if (ast[1] == PLUS && ast[2][0] == UNARY_PREFIX && ast[2][1] == MINUS && ast[2][2]->isNumber()) { + if (ast[1] == PLUS && ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == MINUS && ast[2][2]->isNumber()) { return Literal((double)-ast[2][2]->getNumber()); } - if (ast[1] == MINUS && ast[2][0] == UNARY_PREFIX && ast[2][1] == PLUS && ast[2][2]->isNumber()) { + if (ast[1] == MINUS && ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS && ast[2][2]->isNumber()) { return Literal((double)-ast[2][2]->getNumber()); } - } else if (wasmOnly && ast[0] == CALL && ast[1][0] == NAME && ast[1][1] == I64_CONST) { + } else if (wasmOnly && ast->isArray(CALL) && ast[1]->isString() && ast[1] == I64_CONST) { uint64_t low = ast[2][0]->getNumber(); uint64_t high = ast[2][1]->getNumber(); return Literal(uint64_t(low + (high << 32))); @@ -578,9 +578,9 @@ void Asm2WasmBuilder::processAsm(Ref ast) { assert(imported[0] == DOT); Ref module = imported[1]; IString moduleName; - if (module[0] == DOT) { + if (module->isArray(DOT)) { // we can have (global.Math).floor; skip the 'Math' - assert(module[1][0] == NAME); + assert(module[1]->isString()); if (module[2] == MATH) { if (imported[2] == IMUL) { assert(Math_imul.isNull()); @@ -620,13 +620,13 @@ void Asm2WasmBuilder::processAsm(Ref ast) { return; } } - std::string fullName = module[1][1]->getCString(); + std::string fullName = module[1]->getCString(); fullName += '.'; fullName += + module[2]->getCString(); moduleName = IString(fullName.c_str(), false); } else { - assert(module[0] == NAME); - moduleName = module[1]->getIString(); + assert(module->isString()); + moduleName = module->getIString(); if (moduleName == ENV) { auto base = imported[2]->getIString(); if (base == TEMP_DOUBLE_PTR) { @@ -731,12 +731,12 @@ void Asm2WasmBuilder::processAsm(Ref ast) { addImport(name, import, WasmType::f64); } } else if (value[0] == CALL) { - assert(value[1][0] == NAME && value[1][1] == Math_fround && value[2][0]->isNumber() && value[2][0]->getNumber() == 0); + assert(value[1]->isString() && value[1] == Math_fround && value[2][0]->isNumber() && value[2][0]->getNumber() == 0); allocateGlobal(name, WasmType::f32); } else if (value[0] == DOT) { // simple module.base import. can be a view, or a function. - if (value[1][0] == NAME) { - IString module = value[1][1]->getIString(); + if (value[1]->isString()) { + IString module = value[1]->getIString(); IString base = value[2]->getIString(); if (module == GLOBAL) { if (base == INT8ARRAY) { @@ -768,7 +768,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { bool integer, signed_; AsmType asmType; Ref constructor = value[1]; - if (constructor[0] == DOT) { // global.*Array + if (constructor->isArray(DOT)) { // global.*Array IString heap = constructor[2]->getIString(); if (heap == INT8ARRAY) { bytes = 1; integer = true; signed_ = true; asmType = ASM_INT; @@ -790,8 +790,8 @@ void Asm2WasmBuilder::processAsm(Ref ast) { abort_on("invalid view import", heap); } } else { // *ArrayView that was previously imported - assert(constructor[0] == NAME); - IString viewName = constructor[1]->getIString(); + assert(constructor->isString()); + IString viewName = constructor->getIString(); if (viewName == Int8Array) { bytes = 1; integer = true; signed_ = true; asmType = ASM_INT; } else if (viewName == Int16Array) { @@ -826,7 +826,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { functionTableStarts[name] = segment.data.size(); // this table starts here Ref contents = value[1]; for (unsigned k = 0; k < contents->size(); k++) { - IString curr = contents[k][1]->getIString(); + IString curr = contents[k]->getIString(); segment.data.push_back(curr); } wasm.table.initial = wasm.table.max = segment.data.size(); @@ -850,9 +850,9 @@ void Asm2WasmBuilder::processAsm(Ref ast) { for (unsigned k = 0; k < contents->size(); k++) { Ref pair = contents[k]; IString key = pair[0]->getIString(); - if (pair[1]->isArray(NAME)) { + if (pair[1]->isString()) { // exporting a function - IString value = pair[1][1]->getIString(); + IString value = pair[1]->getIString(); if (key == Name("_emscripten_replace_memory")) { // asm.js memory growth provides this special non-asm function, which we don't need (we use grow_memory) assert(!wasm.checkFunction(value)); @@ -1194,8 +1194,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Ref curr = body[i]; assert(curr[0] == STAT); curr = curr[1]; - assert(curr[0] == ASSIGN && curr[2][0] == NAME); - IString name = curr[2][1]->getIString(); + assert(curr[0] == ASSIGN && curr[2]->isString()); + IString name = curr[2]->getIString(); AsmType asmType = detectType(curr[3], nullptr, false, Math_fround, wasmOnly); Builder::addParam(function, name, asmToWasmType(asmType)); functionVariables.insert(name); @@ -1231,6 +1231,37 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { std::function process = [&](Ref ast) -> Expression* { AstStackHelper astStackHelper(ast); // TODO: only create one when we need it? + if (ast->isString()) { + IString name = ast->getIString(); + if (functionVariables.has(name)) { + // var in scope + auto ret = allocator.alloc(); + ret->index = function->getLocalIndex(name); + ret->type = asmToWasmType(asmData.getType(name)); + return ret; + } + if (name == DEBUGGER) { + CallImport *call = allocator.alloc(); + call->target = DEBUGGER; + call->type = none; + static bool addedImport = false; + if (!addedImport) { + addedImport = true; + auto import = new Import; // debugger = asm2wasm.debugger; + import->name = DEBUGGER; + import->module = ASM2WASM; + import->base = DEBUGGER; + import->functionType = ensureFunctionType("v", &wasm); + import->kind = ExternalKind::Function; + wasm.addImport(import); + } + return call; + } + // global var + assert(mappedGlobals.find(name) != mappedGlobals.end() ? true : (std::cerr << name.str << '\n', false)); + MappedGlobal& global = mappedGlobals[name]; + return builder.makeGetGlobal(name, global.type); + } if (ast->isNumber()) { auto ret = allocator.alloc(); double num = ast->getNumber(); @@ -1248,11 +1279,11 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { if (what == STAT) { return process(ast[1]); // and drop return value, if any } else if (what == ASSIGN) { - if (ast[2][0] == NAME) { - IString name = ast[2][1]->getIString(); + if (ast[2]->isString()) { + IString name = ast[2]->getIString(); if (functionVariables.has(name)) { auto ret = allocator.alloc(); - ret->index = function->getLocalIndex(ast[2][1]->getIString()); + ret->index = function->getLocalIndex(ast[2]->getIString()); ret->value = process(ast[3]); ret->setTee(false); ret->finalize(); @@ -1266,8 +1297,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { return builder.makeSequence(ret, builder.makeGetGlobal(name, ret->value->type)); } else if (ast[2][0] == SUB) { Ref target = ast[2]; - assert(target[1][0] == NAME); - IString heap = target[1][1]->getIString(); + assert(target[1]->isString()); + IString heap = target[1]->getIString(); assert(views.find(heap) != views.end()); View& view = views[heap]; auto ret = allocator.alloc(); @@ -1357,40 +1388,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { return call; } return ret; - } else if (what == NAME) { - IString name = ast[1]->getIString(); - if (functionVariables.has(name)) { - // var in scope - auto ret = allocator.alloc(); - ret->index = function->getLocalIndex(name); - ret->type = asmToWasmType(asmData.getType(name)); - return ret; - } - if (name == DEBUGGER) { - CallImport *call = allocator.alloc(); - call->target = DEBUGGER; - call->type = none; - static bool addedImport = false; - if (!addedImport) { - addedImport = true; - auto import = new Import; // debugger = asm2wasm.debugger; - import->name = DEBUGGER; - import->module = ASM2WASM; - import->base = DEBUGGER; - import->functionType = ensureFunctionType("v", &wasm); - import->kind = ExternalKind::Function; - wasm.addImport(import); - } - return call; - } - // global var - assert(mappedGlobals.find(name) != mappedGlobals.end() ? true : (std::cerr << name.str << '\n', false)); - MappedGlobal& global = mappedGlobals[name]; - return builder.makeGetGlobal(name, global.type); } else if (what == SUB) { Ref target = ast[1]; - assert(target[0] == NAME); - IString heap = target[1]->getIString(); + assert(target->isString()); + IString heap = target->getIString(); assert(views.find(heap) != views.end()); View& view = views[heap]; auto ret = allocator.alloc(); @@ -1510,8 +1511,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { auto* ifTrue = process(ast[2]); return builder.makeIf(truncateToInt32(condition), ifTrue, !!ast[3] ? process(ast[3]) : nullptr); } else if (what == CALL) { - if (ast[1][0] == NAME) { - IString name = ast[1][1]->getIString(); + if (ast[1]->isString()) { + IString name = ast[1]->getIString(); if (name == Math_imul) { assert(ast[2]->size() == 2); auto ret = allocator.alloc(); @@ -1789,7 +1790,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { // function pointers auto ret = allocator.alloc(); Ref target = ast[1]; - assert(target[0] == SUB && target[1][0] == NAME && target[2][0] == BINARY && target[2][1] == AND && target[2][3]->isNumber()); // FUNCTION_TABLE[(expr) & mask] + assert(target[0] == SUB && target[1]->isString() && target[2][0] == BINARY && target[2][1] == AND && target[2][3]->isNumber()); // FUNCTION_TABLE[(expr) & mask] ret->target = process(target[2]); // TODO: as an optimization, we could look through the mask Ref args = ast[2]; for (unsigned i = 0; i < args->size(); i++) { @@ -1799,7 +1800,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { ret->fullType = fullType->name; ret->type = fullType->result; // we don't know the table offset yet. emit target = target + callImport(tableName), which we fix up later when we know how asm function tables are layed out inside the wasm table. - ret->target = builder.makeBinary(BinaryOp::AddInt32, ret->target, builder.makeCallImport(target[1][1]->getIString(), {}, i32)); + ret->target = builder.makeBinary(BinaryOp::AddInt32, ret->target, builder.makeCallImport(target[1]->getIString(), {}, i32)); return ret; } else if (what == RETURN) { WasmType type = !!ast[1] ? detectWasmType(ast[1], &asmData) : none; @@ -2021,10 +2022,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { // (HEAP32[tempDoublePtr >> 2] = i, Math_fround(HEAPF32[tempDoublePtr >> 2])); // i32->f32 // (HEAP32[tempDoublePtr >> 2] = i, +HEAPF32[tempDoublePtr >> 2]); // i32->f32, no fround // (HEAPF32[tempDoublePtr >> 2] = f, HEAP32[tempDoublePtr >> 2] | 0); // f32->i32 - if (ast[1]->isArray(ASSIGN) && ast[1][2]->isArray(SUB) && ast[1][2][1]->isArray(NAME) && ast[1][2][2]->isArray(BINARY) && ast[1][2][2][1] == RSHIFT && - ast[1][2][2][2]->isArray(NAME) && ast[1][2][2][2][1] == tempDoublePtr && ast[1][2][2][3]->isNumber() && ast[1][2][2][3]->getNumber() == 2) { + if (ast[1]->isArray(ASSIGN) && ast[1][2]->isArray(SUB) && ast[1][2][1]->isString() && ast[1][2][2]->isArray(BINARY) && ast[1][2][2][1] == RSHIFT && + ast[1][2][2][2]->isString() && ast[1][2][2][2] == tempDoublePtr && ast[1][2][2][3]->isNumber() && ast[1][2][2][3]->getNumber() == 2) { // (?[tempDoublePtr >> 2] = ?, ?) so far - auto heap = ast[1][2][1][1]->getIString(); + auto heap = ast[1][2][1]->getIString(); if (views.find(heap) != views.end()) { AsmType writeType = views[heap].type; AsmType readType = ASM_NONE; @@ -2035,13 +2036,13 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { } else if (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS) { readType = ASM_DOUBLE; readValue = ast[2][2]; - } else if (ast[2]->isArray(CALL) && ast[2][1]->isArray(NAME) && ast[2][1][1] == Math_fround) { + } else if (ast[2]->isArray(CALL) && ast[2][1]->isString() && ast[2][1] == Math_fround) { readType = ASM_FLOAT; readValue = ast[2][2][0]; } if (readType != ASM_NONE) { - if (readValue->isArray(SUB) && readValue[1]->isArray(NAME) && readValue[2]->isArray(BINARY) && readValue[2][1] == RSHIFT && - readValue[2][2]->isArray(NAME) && readValue[2][2][1] == tempDoublePtr && readValue[2][3]->isNumber() && readValue[2][3]->getNumber() == 2) { + if (readValue->isArray(SUB) && readValue[1]->isString() && readValue[2]->isArray(BINARY) && readValue[2][1] == RSHIFT && + readValue[2][2]->isString() && readValue[2][2] == tempDoublePtr && readValue[2][3]->isNumber() && readValue[2][3]->getNumber() == 2) { // pattern looks right! Ref writtenValue = ast[1][3]; if (writeType == ASM_INT && (readType == ASM_FLOAT || readType == ASM_DOUBLE)) { diff --git a/src/emscripten-optimizer/optimizer-shared.cpp b/src/emscripten-optimizer/optimizer-shared.cpp index b2c03cf28..7ba6d4a22 100644 --- a/src/emscripten-optimizer/optimizer-shared.cpp +++ b/src/emscripten-optimizer/optimizer-shared.cpp @@ -53,29 +53,26 @@ HeapInfo parseHeap(const char *name) { } AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFround, bool allowI64) { + if (node->isString()) { + if (asmData) { + AsmType ret = asmData->getType(node->getCString()); + if (ret != ASM_NONE) return ret; + } + if (!inVarDef) { + if (node == INF || node == NaN) return ASM_DOUBLE; + if (node == 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->getIString(); + else assert(node == ASM_FLOAT_ZERO); + return ASM_FLOAT; + } if (node->isNumber()) { if (!wasm::isInteger(node->getNumber())) return ASM_DOUBLE; return ASM_INT; } switch (node[0]->getCString()[0]) { - case 'n': { - if (node[0] == NAME) { - if (asmData) { - AsmType ret = asmData->getType(node[1]->getCString()); - if (ret != ASM_NONE) return ret; - } - if (!inVarDef) { - 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]) { @@ -89,8 +86,8 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFr } case 'c': { if (node[0] == CALL) { - if (node[1][0] == NAME) { - IString name = node[1][1]->getIString(); + if (node[1]->isString()) { + IString name = node[1]->getIString(); if (name == MATH_FROUND || name == minifiedFround) return ASM_FLOAT; else if (allowI64 && (name == INT64 || name == INT64_CONST)) return ASM_INT64; else if (name == SIMD_FLOAT32X4 || name == SIMD_FLOAT32X4_CHECK) return ASM_FLOAT32X4; @@ -122,7 +119,7 @@ AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFr if (node[0] == SEQ) { return detectType(node[2], asmData, inVarDef, minifiedFround, allowI64); } else if (node[0] == SUB) { - assert(node[1][0] == NAME); + assert(node[1]->isString()); HeapInfo info = parseHeap(node[1][1]->getCString()); if (info.valid) return ASM_NONE; return info.floaty ? ASM_DOUBLE : ASM_INT; // XXX ASM_FLOAT? @@ -142,6 +139,9 @@ static void abort_on(Ref node) { } AsmSign detectSign(Ref node, IString minifiedFround) { + if (node->isString()) { + return ASM_FLEXIBLE; + } if (node->isNumber()) { double value = node->getNumber(); if (value < 0) return ASM_SIGNED; @@ -170,12 +170,10 @@ AsmSign detectSign(Ref node, IString minifiedFround) { case '~': return ASM_SIGNED; default: abort_on(node); } - } 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; + if (node[1]->isString() && (node[1] == MATH_FROUND || node[1] == minifiedFround)) return ASM_NONSIGNED; } else if (type == SEQ) { return detectSign(node[2], minifiedFround); } diff --git a/src/emscripten-optimizer/parser.cpp b/src/emscripten-optimizer/parser.cpp index d0d5aac54..645762f0a 100644 --- a/src/emscripten-optimizer/parser.cpp +++ b/src/emscripten-optimizer/parser.cpp @@ -25,7 +25,6 @@ IString TOPLEVEL("toplevel"), BLOCK("block"), STAT("stat"), ASSIGN("assign"), - NAME("name"), VAR("var"), CONST("const"), CONDITIONAL("conditional"), diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h index e43709f9b..8b988a6c2 100644 --- a/src/emscripten-optimizer/parser.h +++ b/src/emscripten-optimizer/parser.h @@ -40,7 +40,6 @@ extern IString TOPLEVEL, BLOCK, STAT, ASSIGN, - NAME, VAR, CONST, CONDITIONAL, diff --git a/src/emscripten-optimizer/simple_ast.cpp b/src/emscripten-optimizer/simple_ast.cpp index 4ef6c7f2a..4f36036ae 100644 --- a/src/emscripten-optimizer/simple_ast.cpp +++ b/src/emscripten-optimizer/simple_ast.cpp @@ -277,6 +277,6 @@ void traverseFunctions(Ref ast, std::function visit) { // ValueBuilder -IStringSet ValueBuilder::statable("assign call binary unary-prefix name conditional dot new sub seq string object array"); +IStringSet ValueBuilder::statable("assign call binary unary-prefix 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 bb757e260..e89ddb666 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -663,6 +663,10 @@ struct JSPrinter { void print(Ref node) { ensure(); + if (node->isString()) { + printName(node); + return; + } if (node->isNumber()) { printNum(node); return; @@ -708,8 +712,7 @@ struct JSPrinter { break; } case 'n': { - if (type == NAME) printName(node); - else if (type == NEW) printNew(node); + if (type == NEW) printNew(node); else abort(); break; } @@ -842,7 +845,7 @@ struct JSPrinter { } void printName(Ref node) { - emit(node[1]->getCString()); + emit(node->getCString()); } static char* numToString(double d, bool finalize=true) { @@ -1357,8 +1360,7 @@ public: } static Ref makeName(IString name) { - return &makeRawArray(2)->push_back(makeRawString(NAME)) - .push_back(makeRawString(name)); + return makeRawString(name); } static void setBlockContent(Ref target, Ref block) { @@ -1460,7 +1462,7 @@ public: } static Ref makeStatement(Ref contents) { - if (contents->isNumber() || statable.has(contents[0]->getIString())) { + if (contents->isNumber() || contents->isString() || statable.has(contents[0]->getIString())) { return &makeRawArray(2)->push_back(makeRawString(STAT)) .push_back(contents); } else { @@ -1636,8 +1638,8 @@ public: } static Ref makeDot(Ref obj, Ref key) { - assert(key[0] == NAME); - return makeDot(obj, key[1]->getIString()); + assert(key->isString()); + return makeDot(obj, key->getIString()); } static Ref makeNew(Ref call) { -- cgit v1.2.3 From f2af5a9d587cbdeb8f164fb5d37d20daef527213 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 31 Jan 2017 16:08:36 -0800 Subject: refactor asm.js ast to use an Assign node --- src/asm2wasm.h | 128 +++++++++++++------------ src/emscripten-optimizer/parser.cpp | 1 - src/emscripten-optimizer/parser.h | 1 - src/emscripten-optimizer/simple_ast.cpp | 102 +++++++++++++++++++- src/emscripten-optimizer/simple_ast.h | 164 ++++++++++---------------------- 5 files changed, 216 insertions(+), 180 deletions(-) (limited to 'src/emscripten-optimizer/parser.h') diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 13eb393ff..c65e7fedb 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -1194,9 +1194,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Ref curr = body[i]; assert(curr[0] == STAT); curr = curr[1]; - assert(curr[0] == ASSIGN && curr[2]->isString()); - IString name = curr[2]->getIString(); - AsmType asmType = detectType(curr[3], nullptr, false, Math_fround, wasmOnly); + auto* assign = curr->asAssign(); + IString name = assign->target()->getIString(); + AsmType asmType = detectType(assign->value(), nullptr, false, Math_fround, wasmOnly); Builder::addParam(function, name, asmToWasmType(asmType)); functionVariables.insert(name); asmData.addParam(name, asmType); @@ -1275,28 +1275,26 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { ret->type = ret->value.type; return ret; } - IString what = ast[0]->getIString(); - if (what == STAT) { - return process(ast[1]); // and drop return value, if any - } else if (what == ASSIGN) { - if (ast[2]->isString()) { - IString name = ast[2]->getIString(); + if (ast->isAssign()) { + auto* assign = ast->asAssign(); + if (assign->target()->isString()) { + IString name = assign->target()->getIString(); if (functionVariables.has(name)) { auto ret = allocator.alloc(); - ret->index = function->getLocalIndex(ast[2]->getIString()); - ret->value = process(ast[3]); + ret->index = function->getLocalIndex(assign->target()->getIString()); + ret->value = process(assign->value()); ret->setTee(false); ret->finalize(); return ret; } // global var assert(mappedGlobals.find(name) != mappedGlobals.end()); - auto* ret = builder.makeSetGlobal(name, process(ast[3])); + auto* ret = builder.makeSetGlobal(name, process(assign->value())); // set_global does not return; if our value is trivially not used, don't emit a load (if nontrivially not used, opts get it later) - if (astStackHelper.getParent()[0] == STAT) return ret; + if (astStackHelper.getParent()->isArray(STAT)) return ret; return builder.makeSequence(ret, builder.makeGetGlobal(name, ret->value->type)); - } else if (ast[2][0] == SUB) { - Ref target = ast[2]; + } else if (assign->target()->isArray(SUB)) { + Ref target = assign->target(); assert(target[1]->isString()); IString heap = target[1]->getIString(); assert(views.find(heap) != views.end()); @@ -1306,7 +1304,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { ret->offset = 0; ret->align = view.bytes; ret->ptr = processUnshifted(target[2], view.bytes); - ret->value = process(ast[3]); + ret->value = process(assign->value()); ret->valueType = asmToWasmType(view.type); ret->finalize(); if (ret->valueType != ret->value->type) { @@ -1330,6 +1328,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { return ret; } abort_on("confusing assign", ast); + } + IString what = ast[0]->getIString(); + if (what == STAT) { + return process(ast[1]); // and drop return value, if any } else if (what == BINARY) { if ((ast[1] == OR || ast[1] == TRSHIFT) && ast[3]->isNumber() && ast[3]->getNumber() == 0) { auto ret = process(ast[2]); // just look through the ()|0 or ()>>>0 coercion @@ -2022,52 +2024,56 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { // (HEAP32[tempDoublePtr >> 2] = i, Math_fround(HEAPF32[tempDoublePtr >> 2])); // i32->f32 // (HEAP32[tempDoublePtr >> 2] = i, +HEAPF32[tempDoublePtr >> 2]); // i32->f32, no fround // (HEAPF32[tempDoublePtr >> 2] = f, HEAP32[tempDoublePtr >> 2] | 0); // f32->i32 - if (ast[1]->isArray(ASSIGN) && ast[1][2]->isArray(SUB) && ast[1][2][1]->isString() && ast[1][2][2]->isArray(BINARY) && ast[1][2][2][1] == RSHIFT && - ast[1][2][2][2]->isString() && ast[1][2][2][2] == tempDoublePtr && ast[1][2][2][3]->isNumber() && ast[1][2][2][3]->getNumber() == 2) { - // (?[tempDoublePtr >> 2] = ?, ?) so far - auto heap = ast[1][2][1]->getIString(); - if (views.find(heap) != views.end()) { - AsmType writeType = views[heap].type; - AsmType readType = ASM_NONE; - Ref readValue; - if (ast[2]->isArray(BINARY) && ast[2][1] == OR && ast[2][3]->isNumber() && ast[2][3]->getNumber() == 0) { - readType = ASM_INT; - readValue = ast[2][2]; - } else if (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS) { - readType = ASM_DOUBLE; - readValue = ast[2][2]; - } else if (ast[2]->isArray(CALL) && ast[2][1]->isString() && ast[2][1] == Math_fround) { - readType = ASM_FLOAT; - readValue = ast[2][2][0]; - } - if (readType != ASM_NONE) { - if (readValue->isArray(SUB) && readValue[1]->isString() && readValue[2]->isArray(BINARY) && readValue[2][1] == RSHIFT && - readValue[2][2]->isString() && readValue[2][2] == tempDoublePtr && readValue[2][3]->isNumber() && readValue[2][3]->getNumber() == 2) { - // pattern looks right! - Ref writtenValue = ast[1][3]; - if (writeType == ASM_INT && (readType == ASM_FLOAT || readType == ASM_DOUBLE)) { - auto conv = allocator.alloc(); - conv->op = ReinterpretInt32; - conv->value = process(writtenValue); - conv->type = WasmType::f32; - if (readType == ASM_DOUBLE) { - auto promote = allocator.alloc(); - promote->op = PromoteFloat32; - promote->value = conv; - promote->type = WasmType::f64; - return promote; - } - return conv; - } else if (writeType == ASM_FLOAT && readType == ASM_INT) { - auto conv = allocator.alloc(); - conv->op = ReinterpretFloat32; - conv->value = process(writtenValue); - if (conv->value->type == f64) { - // this has an implicit f64->f32 in the write to memory - conv->value = builder.makeUnary(DemoteFloat64, conv->value); + if (ast[1]->isAssign()) { + auto* assign = ast[1]->asAssign(); + Ref target = assign->target(); + if (target->isArray(SUB) && target[1]->isString() && target[2]->isArray(BINARY) && target[2][1] == RSHIFT && + target[2][2]->isString() && target[2][2] == tempDoublePtr && target[2][3]->isNumber() && target[2][3]->getNumber() == 2) { + // (?[tempDoublePtr >> 2] = ?, ?) so far + auto heap = target[1]->getIString(); + if (views.find(heap) != views.end()) { + AsmType writeType = views[heap].type; + AsmType readType = ASM_NONE; + Ref readValue; + if (ast[2]->isArray(BINARY) && ast[2][1] == OR && ast[2][3]->isNumber() && ast[2][3]->getNumber() == 0) { + readType = ASM_INT; + readValue = ast[2][2]; + } else if (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS) { + readType = ASM_DOUBLE; + readValue = ast[2][2]; + } else if (ast[2]->isArray(CALL) && ast[2][1]->isString() && ast[2][1] == Math_fround) { + readType = ASM_FLOAT; + readValue = ast[2][2][0]; + } + if (readType != ASM_NONE) { + if (readValue->isArray(SUB) && readValue[1]->isString() && readValue[2]->isArray(BINARY) && readValue[2][1] == RSHIFT && + readValue[2][2]->isString() && readValue[2][2] == tempDoublePtr && readValue[2][3]->isNumber() && readValue[2][3]->getNumber() == 2) { + // pattern looks right! + Ref writtenValue = assign->value(); + if (writeType == ASM_INT && (readType == ASM_FLOAT || readType == ASM_DOUBLE)) { + auto conv = allocator.alloc(); + conv->op = ReinterpretInt32; + conv->value = process(writtenValue); + conv->type = WasmType::f32; + if (readType == ASM_DOUBLE) { + auto promote = allocator.alloc(); + promote->op = PromoteFloat32; + promote->value = conv; + promote->type = WasmType::f64; + return promote; + } + return conv; + } else if (writeType == ASM_FLOAT && readType == ASM_INT) { + auto conv = allocator.alloc(); + conv->op = ReinterpretFloat32; + conv->value = process(writtenValue); + if (conv->value->type == f64) { + // this has an implicit f64->f32 in the write to memory + conv->value = builder.makeUnary(DemoteFloat64, conv->value); + } + conv->type = WasmType::i32; + return conv; } - conv->type = WasmType::i32; - return conv; } } } diff --git a/src/emscripten-optimizer/parser.cpp b/src/emscripten-optimizer/parser.cpp index 645762f0a..e85947e22 100644 --- a/src/emscripten-optimizer/parser.cpp +++ b/src/emscripten-optimizer/parser.cpp @@ -24,7 +24,6 @@ IString TOPLEVEL("toplevel"), DEFUN("defun"), BLOCK("block"), STAT("stat"), - ASSIGN("assign"), VAR("var"), CONST("const"), CONDITIONAL("conditional"), diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h index 8b988a6c2..20e936a76 100644 --- a/src/emscripten-optimizer/parser.h +++ b/src/emscripten-optimizer/parser.h @@ -39,7 +39,6 @@ extern IString TOPLEVEL, DEFUN, BLOCK, STAT, - ASSIGN, VAR, CONST, CONDITIONAL, diff --git a/src/emscripten-optimizer/simple_ast.cpp b/src/emscripten-optimizer/simple_ast.cpp index 7379e9740..f28ef9b13 100644 --- a/src/emscripten-optimizer/simple_ast.cpp +++ b/src/emscripten-optimizer/simple_ast.cpp @@ -56,6 +56,106 @@ bool Ref::operator!() { GlobalMixedArena arena; +// Value + +Value& Value::setAssign(Ref target, Ref value) { + asAssign()->target() = target; + asAssign()->value() = value; + return *this; +} + +Assign* Value::asAssign() { + assert(isAssign()); + return static_cast(this); +} + +void Value::stringify(std::ostream &os, bool pretty) { + static int indent = 0; + #define indentify() { for (int i_ = 0; i_ < indent; i_++) os << " "; } + switch (type) { + case String: { + if (str.str) { + os << '"' << str.str << '"'; + } else { + os << "\"(null)\""; + } + break; + } + case Number: { + os << std::setprecision(17) << num; // doubles can have 17 digits of precision + break; + } + case Array: { + if (arr->size() == 0) { + os << "[]"; + break; + } + os << '['; + if (pretty) { + os << std::endl; + indent++; + } + for (size_t i = 0; i < arr->size(); i++) { + if (i > 0) { + if (pretty) os << "," << std::endl; + else os << ", "; + } + indentify(); + (*arr)[i]->stringify(os, pretty); + } + if (pretty) { + os << std::endl; + indent--; + } + indentify(); + os << ']'; + break; + } + case Null: { + os << "null"; + break; + } + case Bool: { + os << (boo ? "true" : "false"); + break; + } + case Object: { + os << '{'; + if (pretty) { + os << std::endl; + indent++; + } + bool first = true; + for (auto i : *obj) { + if (first) { + first = false; + } else { + os << ", "; + if (pretty) os << std::endl; + } + indentify(); + os << '"' << i.first.c_str() << "\": "; + i.second->stringify(os, pretty); + } + if (pretty) { + os << std::endl; + indent--; + } + indentify(); + os << '}'; + break; + } + case Assign_: { + os << "["; + ref->stringify(os, pretty); + os << ", "; + asAssign()->value()->stringify(os, pretty); + os << "]"; + break; + } + } +} + // dump void dump(const char *str, Ref node, bool pretty) { @@ -252,6 +352,6 @@ void traverseFunctions(Ref ast, std::function visit) { // ValueBuilder -IStringSet ValueBuilder::statable("assign call binary unary-prefix conditional dot new sub seq string object array"); +IStringSet ValueBuilder::statable("call binary unary-prefix 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 f4dce76aa..784f49632 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -44,8 +44,8 @@ namespace cashew { -struct Ref; struct Value; +struct Ref; void dump(const char *str, Ref node, bool pretty=false); @@ -96,6 +96,8 @@ public: } }; +struct Assign; + // Main value type struct Value { enum Type { @@ -104,7 +106,8 @@ struct Value { Array = 2, Null = 3, Bool = 4, - Object = 5 + Object = 5, + Assign_ = 6 // ref = target }; Type type; @@ -122,6 +125,7 @@ struct Value { ArrayStorage *arr; bool boo; ObjectStorage *obj; + Ref ref; }; // constructors all copy their input @@ -198,13 +202,15 @@ struct Value { obj = new ObjectStorage(); return *this; } + Value& setAssign(Ref target, Ref value); bool isString() { return type == String; } bool isNumber() { return type == Number; } bool isArray() { return type == Array; } bool isNull() { return type == Null; } bool isBool() { return type == Bool; } - bool isObject() { return type == Object; } + bool isObject() { return type == Object; } + bool isAssign() { return type == Assign_; } bool isBool(bool b) { return type == Bool && b == boo; } // avoid overloading == as it might overload over int @@ -237,6 +243,8 @@ struct Value { return boo; } + Assign* asAssign(); + int32_t getInteger() { // convenience function to get a known integer assert(fmod(getNumber(), 1) == 0); int32_t ret = getNumber(); @@ -262,7 +270,7 @@ struct Value { case Bool: setBool(other.boo); break; - case Object: + default: abort(); // TODO } return *this; @@ -283,31 +291,12 @@ struct Value { return boo == other.boo; case Object: return this == &other; // if you want a deep compare, use deepCompare + default: + abort(); } return true; } - bool deepCompare(Ref ref) { - Value& other = *ref; - if (*this == other) return true; // either same pointer, or identical value type (string, number, null or bool) - if (type != other.type) return false; - if (type == Array) { - if (arr->size() != other.arr->size()) return false; - for (unsigned i = 0; i < arr->size(); i++) { - if (!(*arr)[i]->deepCompare((*other.arr)[i])) return false; - } - return true; - } else if (type == Object) { - if (obj->size() != other.obj->size()) return false; - for (auto i : *obj) { - if (other.obj->count(i.first) == 0) return false; - if (i.second->deepCompare((*other.obj)[i.first])) return false; - } - return true; - } - return false; - } - char* parse(char* curr) { #define is_json_space(x) (x == 32 || x == 9 || x == 10 || x == 13) /* space, tab, linefeed/newline, or return */ #define skip() { while (*curr && is_json_space(*curr)) curr++; } @@ -387,78 +376,7 @@ struct Value { return curr; } - void stringify(std::ostream &os, bool pretty=false) { - static int indent = 0; - #define indentify() { for (int i_ = 0; i_ < indent; i_++) os << " "; } - switch (type) { - case String: - if (str.str) { - os << '"' << str.str << '"'; - } else { - os << "\"(null)\""; - } - break; - case Number: - os << std::setprecision(17) << num; // doubles can have 17 digits of precision - break; - case Array: - if (arr->size() == 0) { - os << "[]"; - break; - } - os << '['; - if (pretty) { - os << std::endl; - indent++; - } - for (size_t i = 0; i < arr->size(); i++) { - if (i > 0) { - if (pretty) os << "," << std::endl; - else os << ", "; - } - indentify(); - (*arr)[i]->stringify(os, pretty); - } - if (pretty) { - os << std::endl; - indent--; - } - indentify(); - os << ']'; - break; - case Null: - os << "null"; - break; - case Bool: - os << (boo ? "true" : "false"); - break; - case Object: - os << '{'; - if (pretty) { - os << std::endl; - indent++; - } - bool first = true; - for (auto i : *obj) { - if (first) { - first = false; - } else { - os << ", "; - if (pretty) os << std::endl; - } - indentify(); - os << '"' << i.first.c_str() << "\": "; - i.second->stringify(os, pretty); - } - if (pretty) { - os << std::endl; - indent--; - } - indentify(); - os << '}'; - break; - } - } + void stringify(std::ostream &os, bool pretty=false); // String operations @@ -559,6 +477,25 @@ struct Value { } }; +struct Assign : public Value { + Ref value_; + + Assign(Ref targetInit, Ref valueInit) { + type = Assign_; + target() = targetInit; + value() = valueInit; + } + + Assign() : Assign(nullptr, nullptr) {} + + Ref& target() { + return ref; + } + Ref& value() { + return value_; + } +}; + // AST traversals // Traverse, calling visit before the children @@ -663,12 +600,14 @@ struct JSPrinter { printNum(node); return; } + if (node->isAssign()) { + printAssign(node); + } IString type = node[0]->getIString(); //fprintf(stderr, "printing %s\n", type.str); switch (type.str[0]) { case 'a': { - if (type == ASSIGN) printAssign(node); - else if (type == ARRAY) printArray(node); + if (type == ARRAY) printArray(node); else abort(); break; } @@ -829,11 +768,12 @@ struct JSPrinter { } void printAssign(Ref node) { - printChild(node[2], node, -1); + auto* assign = node->asAssign(); + printChild(assign->target(), node, -1); space(); emit('='); space(); - printChild(node[3], node, 1); + printChild(assign->value(), node, 1); } void printName(Ref node) { @@ -980,6 +920,9 @@ struct JSPrinter { } int getPrecedence(Ref node, bool parent) { + if (node->isAssign()) { + return OperatorClass::getPrecedence(OperatorClass::Binary, SET); + } Ref type = node[0]; if (type == BINARY || type == UNARY_PREFIX) { return OperatorClass::getPrecedence(type == BINARY ? OperatorClass::Binary : OperatorClass::Prefix, node[1]->getIString()); @@ -987,8 +930,6 @@ struct JSPrinter { return OperatorClass::getPrecedence(OperatorClass::Binary, COMMA); } else if (type == CALL) { return parent ? OperatorClass::getPrecedence(OperatorClass::Binary, COMMA) : -1; // call arguments are split by commas, but call itself is safe - } else if (type == ASSIGN) { - return OperatorClass::getPrecedence(OperatorClass::Binary, SET); } else if (type == CONDITIONAL) { return OperatorClass::getPrecedence(OperatorClass::Tertiary, QUESTION); } @@ -1454,7 +1395,7 @@ public: } static Ref makeStatement(Ref contents) { - if (contents->isNumber() || contents->isString() || statable.has(contents[0]->getIString())) { + if (contents->isNumber() || contents->isString() || contents->isAssign() || statable.has(contents[0]->getIString())) { return &makeRawArray(2)->push_back(makeRawString(STAT)) .push_back(contents); } else { @@ -1480,10 +1421,7 @@ public: static Ref makeBinary(Ref left, IString op, Ref right) { if (op == SET) { - return &makeRawArray(4)->push_back(makeRawString(ASSIGN)) - .push_back(&arena.alloc()->setBool(true)) - .push_back(left) - .push_back(right); + return &arena.alloc()->setAssign(left, right); } else if (op == COMMA) { return &makeRawArray(3)->push_back(makeRawString(SEQ)) .push_back(left) @@ -1661,16 +1599,10 @@ public: } 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); + return &arena.alloc()->setAssign(target, 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); + return &arena.alloc()->setAssign(makeName(target), value); } static Ref makeSub(Ref obj, Ref index) { -- cgit v1.2.3 From f6c0bc26b65c6cfd58d1bb011c1587d343c42ab5 Mon Sep 17 00:00:00 2001 From: "Alon Zakai (kripken)" Date: Tue, 31 Jan 2017 20:03:41 -0800 Subject: refactor asm.js ast to not use STAT nodes - we don't need to print the asm.js anyhow, so knowing where ;s are is unnecessary bloat --- src/asm2wasm.h | 13 +- src/emscripten-optimizer/optimizer.h | 5 - src/emscripten-optimizer/parser.cpp | 1 - src/emscripten-optimizer/parser.h | 1 - src/emscripten-optimizer/simple_ast.cpp | 4 - src/emscripten-optimizer/simple_ast.h | 634 +------------------------------- 6 files changed, 7 insertions(+), 651 deletions(-) (limited to 'src/emscripten-optimizer/parser.h') diff --git a/src/asm2wasm.h b/src/asm2wasm.h index c65e7fedb..1d063a8ee 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -572,7 +572,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) { Ref asmFunction = ast[1][0]; assert(asmFunction[0] == DEFUN); Ref body = asmFunction[3]; - assert(body[0][0] == STAT && body[0][1][0] == STRING && (body[0][1][1]->getIString() == IString("use asm") || body[0][1][1]->getIString() == IString("almost asm"))); + assert(body[0][0] == STRING && (body[0][1]->getIString() == IString("use asm") || body[0][1]->getIString() == IString("almost asm"))); auto addImport = [&](IString name, Ref imported, WasmType type) { assert(imported[0] == DOT); @@ -1192,8 +1192,6 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { for (unsigned i = 0; i < params->size(); i++) { Ref curr = body[i]; - assert(curr[0] == STAT); - curr = curr[1]; auto* assign = curr->asAssign(); IString name = assign->target()->getIString(); AsmType asmType = detectType(assign->value(), nullptr, false, Math_fround, wasmOnly); @@ -1202,7 +1200,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { asmData.addParam(name, asmType); } unsigned start = params->size(); - while (start < body->size() && body[start][0] == VAR) { + while (start < body->size() && body[start]->isArray(VAR)) { Ref curr = body[start]; for (unsigned j = 0; j < curr[1]->size(); j++) { Ref pair = curr[1][j]; @@ -1291,7 +1289,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { assert(mappedGlobals.find(name) != mappedGlobals.end()); auto* ret = builder.makeSetGlobal(name, process(assign->value())); // set_global does not return; if our value is trivially not used, don't emit a load (if nontrivially not used, opts get it later) - if (astStackHelper.getParent()->isArray(STAT)) return ret; + auto parent = astStackHelper.getParent(); + if (!parent || parent->isArray(BLOCK) || parent->isArray(IF)) return ret; return builder.makeSequence(ret, builder.makeGetGlobal(name, ret->value->type)); } else if (assign->target()->isArray(SUB)) { Ref target = assign->target(); @@ -1330,9 +1329,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { abort_on("confusing assign", ast); } IString what = ast[0]->getIString(); - if (what == STAT) { - return process(ast[1]); // and drop return value, if any - } else if (what == BINARY) { + if (what == BINARY) { if ((ast[1] == OR || ast[1] == TRSHIFT) && ast[3]->isNumber() && ast[3]->getNumber() == 0) { auto ret = process(ast[2]); // just look through the ()|0 or ()>>>0 coercion fixCallType(ret, i32); diff --git a/src/emscripten-optimizer/optimizer.h b/src/emscripten-optimizer/optimizer.h index dc73962c5..c3939abf4 100644 --- a/src/emscripten-optimizer/optimizer.h +++ b/src/emscripten-optimizer/optimizer.h @@ -144,11 +144,6 @@ enum AsmSign { 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); diff --git a/src/emscripten-optimizer/parser.cpp b/src/emscripten-optimizer/parser.cpp index e85947e22..227ce66a5 100644 --- a/src/emscripten-optimizer/parser.cpp +++ b/src/emscripten-optimizer/parser.cpp @@ -23,7 +23,6 @@ namespace cashew { IString TOPLEVEL("toplevel"), DEFUN("defun"), BLOCK("block"), - STAT("stat"), VAR("var"), CONST("const"), CONDITIONAL("conditional"), diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h index 20e936a76..5c9aa2c8c 100644 --- a/src/emscripten-optimizer/parser.h +++ b/src/emscripten-optimizer/parser.h @@ -38,7 +38,6 @@ namespace cashew { extern IString TOPLEVEL, DEFUN, BLOCK, - STAT, VAR, CONST, CONDITIONAL, diff --git a/src/emscripten-optimizer/simple_ast.cpp b/src/emscripten-optimizer/simple_ast.cpp index f28ef9b13..618b0d9dd 100644 --- a/src/emscripten-optimizer/simple_ast.cpp +++ b/src/emscripten-optimizer/simple_ast.cpp @@ -350,8 +350,4 @@ void traverseFunctions(Ref ast, std::function visit) { } } -// ValueBuilder - -IStringSet ValueBuilder::statable("call binary unary-prefix 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 784f49632..aa02dd8fc 100644 --- a/src/emscripten-optimizer/simple_ast.h +++ b/src/emscripten-optimizer/simple_ast.h @@ -510,276 +510,9 @@ void traversePrePostConditional(Ref node, std::function visitPre, st // Traverses all the top-level functions in the document void traverseFunctions(Ref ast, std::function visit); -// JS printer +// JS printing support struct JSPrinter { - bool pretty, finalize; - - char *buffer; - size_t size, used; - - int indent; - bool possibleSpace; // add a space to separate identifiers - - Ref ast; - - JSPrinter(bool pretty_, bool finalize_, Ref ast_) : pretty(pretty_), finalize(finalize_), buffer(0), size(0), used(0), indent(0), possibleSpace(false), ast(ast_) {} - - void printAst() { - print(ast); - buffer[used] = 0; - } - - // Utils - - void ensure(int safety=100) { - if (size < used + safety) { - size = std::max((size_t)1024, size * 2) + safety; - if (!buffer) { - buffer = (char*)malloc(size); - if (!buffer) { - printf("Out of memory allocating %zd bytes for output buffer!", size); - abort(); - } - } else { - char *buf = (char*)realloc(buffer, size); - if (!buf) { - free(buffer); - printf("Out of memory allocating %zd bytes for output buffer!", size); - abort(); - } - buffer = buf; - } - } - } - - void emit(char c) { - maybeSpace(c); - if (!pretty && c == '}' && buffer[used-1] == ';') used--; // optimize ;} into }, the ; is not separating anything - ensure(1); - buffer[used++] = c; - } - - void emit(const char *s) { - maybeSpace(*s); - int len = strlen(s); - ensure(len+1); - strncpy(buffer + used, s, len+1); - used += len; - } - - void newline() { - if (!pretty) return; - emit('\n'); - for (int i = 0; i < indent; i++) emit(' '); - } - - void space() { - if (pretty) emit(' '); - } - - void safeSpace() { - if (pretty) emit(' '); - else possibleSpace = true; - } - - void maybeSpace(char s) { - if (possibleSpace) { - possibleSpace = false; - if (isIdentPart(s)) emit(' '); - } - } - - void print(Ref node) { - ensure(); - if (node->isString()) { - printName(node); - return; - } - if (node->isNumber()) { - printNum(node); - return; - } - if (node->isAssign()) { - printAssign(node); - } - IString type = node[0]->getIString(); - //fprintf(stderr, "printing %s\n", type.str); - switch (type.str[0]) { - case 'a': { - if (type == ARRAY) printArray(node); - else abort(); - break; - } - case 'b': { - if (type == BINARY) printBinary(node); - else if (type == BLOCK) printBlock(node); - else if (type == BREAK) printBreak(node); - else abort(); - break; - } - case 'c': { - if (type == CALL) printCall(node); - else if (type == CONDITIONAL) printConditional(node); - else if (type == CONTINUE) printContinue(node); - else abort(); - break; - } - case 'd': { - if (type == DEFUN) printDefun(node); - else if (type == DO) printDo(node); - else if (type == DOT) printDot(node); - else abort(); - break; - } - case 'i': { - if (type == IF) printIf(node); - else abort(); - break; - } - case 'l': { - if (type == LABEL) printLabel(node); - else abort(); - break; - } - case 'n': { - if (type == NEW) printNew(node); - else abort(); - break; - } - case 'o': { - if (type == OBJECT) printObject(node); - break; - } - case 'r': { - if (type == RETURN) printReturn(node); - else abort(); - break; - } - case 's': { - if (type == STAT) printStat(node); - else if (type == SUB) printSub(node); - else if (type == SEQ) printSeq(node); - else if (type == SWITCH) printSwitch(node); - else if (type == STRING) printString(node); - else abort(); - break; - } - case 't': { - if (type == TOPLEVEL) printToplevel(node); - else abort(); - break; - } - case 'u': { - if (type == UNARY_PREFIX) printUnaryPrefix(node); - else abort(); - break; - } - case 'v': { - if (type == VAR) printVar(node); - else abort(); - break; - } - case 'w': { - if (type == WHILE) printWhile(node); - else abort(); - break; - } - default: { - printf("cannot yet print %s\n", type.str); - abort(); - } - } - } - - // print a node, and if nothing is emitted, emit something instead - void print(Ref node, const char *otherwise) { - auto last = used; - print(node); - if (used == last) emit(otherwise); - } - - void printStats(Ref stats) { - bool first = true; - for (size_t i = 0; i < stats->size(); i++) { - Ref curr = stats[i]; - if (!isNothing(curr)) { - if (first) first = false; - else newline(); - print(stats[i]); - } - } - } - - void printToplevel(Ref node) { - if (node[1]->size() > 0) { - printStats(node[1]); - } - } - - void printBlock(Ref node) { - if (node->size() == 1 || node[1]->size() == 0) { - emit("{}"); - return; - } - emit('{'); - indent++; - newline(); - printStats(node[1]); - indent--; - newline(); - emit('}'); - } - - void printDefun(Ref node) { - emit("function "); - emit(node[1]->getCString()); - emit('('); - Ref args = node[2]; - for (size_t i = 0; i < args->size(); i++) { - if (i > 0) (pretty ? emit(", ") : emit(',')); - emit(args[i]->getCString()); - } - emit(')'); - space(); - if (node->size() == 3 || node[3]->size() == 0) { - emit("{}"); - return; - } - emit('{'); - indent++; - newline(); - printStats(node[3]); - indent--; - newline(); - emit('}'); - newline(); - } - - bool isNothing(Ref node) { - return (node[0] == TOPLEVEL && node[1]->size() == 0) || (node[0] == STAT && isNothing(node[1])); - } - - void printStat(Ref node) { - if (!isNothing(node[1])) { - print(node[1]); - if (buffer[used-1] != ';') emit(';'); - } - } - - void printAssign(Ref node) { - auto* assign = node->asAssign(); - printChild(assign->target(), node, -1); - space(); - emit('='); - space(); - printChild(assign->value(), node, 1); - } - - void printName(Ref node) { - emit(node->getCString()); - } - static char* numToString(double d, bool finalize=true) { bool neg = d < 0; if (neg) d = -d; @@ -901,369 +634,11 @@ struct JSPrinter { } return ret; } - - void printNum(Ref node) { - emit(numToString(node->getNumber(), finalize)); - } - - void printString(Ref node) { - emit('"'); - emit(node[1]->getCString()); - emit('"'); - } - - // Parens optimizing - - bool capturesOperators(Ref node) { - Ref type = node[0]; - return type == CALL || type == ARRAY || type == OBJECT || type == SEQ; - } - - int getPrecedence(Ref node, bool parent) { - if (node->isAssign()) { - return OperatorClass::getPrecedence(OperatorClass::Binary, SET); - } - Ref type = node[0]; - if (type == BINARY || type == UNARY_PREFIX) { - return OperatorClass::getPrecedence(type == BINARY ? OperatorClass::Binary : OperatorClass::Prefix, node[1]->getIString()); - } else if (type == SEQ) { - return OperatorClass::getPrecedence(OperatorClass::Binary, COMMA); - } else if (type == CALL) { - return parent ? OperatorClass::getPrecedence(OperatorClass::Binary, COMMA) : -1; // call arguments are split by commas, but call itself is safe - } else if (type == CONDITIONAL) { - return OperatorClass::getPrecedence(OperatorClass::Tertiary, QUESTION); - } - // otherwise, this is something that fixes precedence explicitly, and we can ignore - return -1; // XXX - } - - // check whether we need parens for the child, when rendered in the parent - // @param childPosition -1 means it is printed to the left of parent, 0 means "anywhere", 1 means right - bool needParens(Ref parent, Ref child, int childPosition) { - int parentPrecedence = getPrecedence(parent, true); - int childPrecedence = getPrecedence(child, false); - - if (childPrecedence > parentPrecedence) return true; // child is definitely a danger - if (childPrecedence < parentPrecedence) return false; // definitely cool - // equal precedence, so associativity (rtl/ltr) is what matters - // (except for some exceptions, where multiple operators can combine into confusion) - if (parent[0] == UNARY_PREFIX) { - assert(child[0] == UNARY_PREFIX); - if ((parent[1] == PLUS || parent[1] == MINUS) && child[1] == parent[1]) { - // cannot emit ++x when we mean +(+x) - return true; - } - } - if (childPosition == 0) return true; // child could be anywhere, so always paren - if (childPrecedence < 0) return false; // both precedences are safe - // check if child is on the dangerous side - if (OperatorClass::getRtl(parentPrecedence)) return childPosition < 0; - else return childPosition > 0; - } - - void printChild(Ref child, Ref parent, int childPosition=0) { - bool parens = needParens(parent, child, childPosition); - if (parens) emit('('); - print(child); - if (parens) emit(')'); - } - - void printBinary(Ref node) { - printChild(node[2], node, -1); - space(); - emit(node[1]->getCString()); - space(); - printChild(node[3], node, 1); - } - - void printUnaryPrefix(Ref node) { - if (finalize && node[1] == PLUS && (node[2]->isNumber() || - (node[2][0] == UNARY_PREFIX && node[2][1] == MINUS && node[2][2]->isNumber()))) { - // emit a finalized number - int last = used; - print(node[2]); - ensure(1); // we temporarily append a 0 - char *curr = buffer + last; // ensure might invalidate - buffer[used] = 0; - if (strchr(curr, '.')) return; // already a decimal point, all good - char *e = strchr(curr, 'e'); - if (!e) { - emit(".0"); - return; - } - ensure(3); - curr = buffer + last; // ensure might invalidate - char *end = strchr(curr, 0); - while (end >= e) { - end[2] = end[0]; - end--; - } - e[0] = '.'; - e[1] = '0'; - used += 2; - return; - } - if ((buffer[used-1] == '-' && node[1] == MINUS) || - (buffer[used-1] == '+' && node[1] == PLUS)) { - emit(' '); // cannot join - and - to --, looks like the -- operator - } - emit(node[1]->getCString()); - printChild(node[2], node, 1); - } - - void printConditional(Ref node) { - printChild(node[1], node, -1); - space(); - emit('?'); - space(); - printChild(node[2], node, 0); - space(); - emit(':'); - space(); - printChild(node[3], node, 1); - } - - void printCall(Ref node) { - printChild(node[1], node, 0); - emit('('); - Ref args = node[2]; - for (size_t i = 0; i < args->size(); i++) { - if (i > 0) (pretty ? emit(", ") : emit(',')); - printChild(args[i], node, 0); - } - emit(')'); - } - - void printSeq(Ref node) { - printChild(node[1], node, -1); - emit(','); - space(); - printChild(node[2], node, 1); - } - - void printDot(Ref node) { - print(node[1]); - emit('.'); - emit(node[2]->getCString()); - } - - void printSwitch(Ref node) { - emit("switch"); - space(); - emit('('); - print(node[1]); - emit(')'); - space(); - emit('{'); - newline(); - Ref cases = node[2]; - for (size_t i = 0; i < cases->size(); i++) { - Ref c = cases[i]; - if (!c[0]) { - emit("default:"); - } else { - emit("case "); - print(c[0]); - emit(':'); - } - if (c[1]->size() > 0) { - indent++; - newline(); - auto curr = used; - printStats(c[1]); - indent--; - if (curr != used) newline(); - else used--; // avoid the extra indentation we added tentatively - } else { - newline(); - } - } - emit('}'); - } - - void printSub(Ref node) { - printChild(node[1], node, -1); - emit('['); - print(node[2]); - emit(']'); - } - - void printVar(Ref node) { - emit("var "); - Ref args = node[1]; - for (size_t i = 0; i < args->size(); i++) { - if (i > 0) (pretty ? emit(", ") : emit(',')); - emit(args[i][0]->getCString()); - if (args[i]->size() > 1) { - space(); - emit('='); - space(); - print(args[i][1]); - } - } - emit(';'); - } - - static bool ifHasElse(Ref node) { - assert(node[0] == IF); - return node->size() >= 4 && !!node[3]; - } - - void printIf(Ref node) { - emit("if"); - safeSpace(); - emit('('); - print(node[1]); - emit(')'); - space(); - // special case: we need braces to save us from ambiguity, if () { if () } else. otherwise else binds to inner if - // also need to recurse for if () { if () { } else { if () } else - // (note that this is only a problem if the if body has a single element in it, not a block or such, as then - // the block would be braced) - // this analysis is a little conservative - it assumes any child if could be confused with us, which implies - // all other braces vanished (the worst case for us, we are not saved by other braces). - bool needBraces = false; - bool hasElse = ifHasElse(node); - if (hasElse) { - Ref child = node[2]; - while (child[0] == IF) { - if (!ifHasElse(child)) { - needBraces = true; - break; - } - child = child[3]; // continue into the else - } - } - if (needBraces) { - emit('{'); - indent++; - newline(); - print(node[2]); - indent--; - newline(); - emit('}'); - } else { - print(node[2], "{}"); - } - if (hasElse) { - space(); - emit("else"); - safeSpace(); - print(node[3], "{}"); - } - } - - void printDo(Ref node) { - emit("do"); - safeSpace(); - print(node[2], "{}"); - space(); - emit("while"); - space(); - emit('('); - print(node[1]); - emit(')'); - emit(';'); - } - - void printWhile(Ref node) { - emit("while"); - space(); - emit('('); - print(node[1]); - emit(')'); - space(); - print(node[2], "{}"); - } - - void printLabel(Ref node) { - emit(node[1]->getCString()); - space(); - emit(':'); - space(); - print(node[2]); - } - - void printReturn(Ref node) { - emit("return"); - if (!!node[1]) { - emit(' '); - print(node[1]); - } - emit(';'); - } - - void printBreak(Ref node) { - emit("break"); - if (!!node[1]) { - emit(' '); - emit(node[1]->getCString()); - } - emit(';'); - } - - void printContinue(Ref node) { - emit("continue"); - if (!!node[1]) { - emit(' '); - emit(node[1]->getCString()); - } - emit(';'); - } - - void printNew(Ref node) { - emit("new "); - print(node[1]); - } - - void printArray(Ref node) { - emit('['); - Ref args = node[1]; - for (size_t i = 0; i < args->size(); i++) { - if (i > 0) (pretty ? emit(", ") : emit(',')); - print(args[i]); - } - emit(']'); - } - - void printObject(Ref node) { - emit('{'); - indent++; - newline(); - Ref args = node[1]; - for (size_t i = 0; i < args->size(); i++) { - if (i > 0) { - pretty ? emit(", ") : emit(','); - newline(); - } - 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]); - } - indent--; - newline(); - emit('}'); - } }; // cashew builder class ValueBuilder { - static IStringSet statable; - static Ref makeRawString(const IString& s) { return &arena.alloc()->setString(s); } @@ -1395,12 +770,7 @@ public: } static Ref makeStatement(Ref contents) { - if (contents->isNumber() || contents->isString() || contents->isAssign() || statable.has(contents[0]->getIString())) { - return &makeRawArray(2)->push_back(makeRawString(STAT)) - .push_back(contents); - } else { - return contents; // only very specific things actually need to be stat'ed - } + return contents; } static Ref makeDouble(double num) { -- cgit v1.2.3