diff options
-rw-r--r-- | src/asm2wasm.h | 128 | ||||
-rw-r--r-- | src/emscripten-optimizer/parser.cpp | 1 | ||||
-rw-r--r-- | src/emscripten-optimizer/parser.h | 1 | ||||
-rw-r--r-- | src/emscripten-optimizer/simple_ast.cpp | 102 | ||||
-rw-r--r-- | src/emscripten-optimizer/simple_ast.h | 164 |
5 files changed, 216 insertions, 180 deletions
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<SetLocal>(); - 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<Unary>(); - conv->op = ReinterpretInt32; - conv->value = process(writtenValue); - conv->type = WasmType::f32; - if (readType == ASM_DOUBLE) { - auto promote = allocator.alloc<Unary>(); - 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<Unary>(); - 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<Unary>(); + conv->op = ReinterpretInt32; + conv->value = process(writtenValue); + conv->type = WasmType::f32; + if (readType == ASM_DOUBLE) { + auto promote = allocator.alloc<Unary>(); + 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<Unary>(); + 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<Assign*>(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<void (Ref)> 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<Value>()->setBool(true)) - .push_back(left) - .push_back(right); + return &arena.alloc<Assign>()->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<Value>()->setBool(true)) - .push_back(target) - .push_back(value); + return &arena.alloc<Assign>()->setAssign(target, value); } static Ref makeAssign(IString target, Ref value) { - return &makeRawArray(3)->push_back(makeRawString(ASSIGN)) - .push_back(&arena.alloc<Value>()->setBool(true)) - .push_back(makeName(target)) - .push_back(value); + return &arena.alloc<Assign>()->setAssign(makeName(target), value); } static Ref makeSub(Ref obj, Ref index) { |