summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/asm2wasm.h128
-rw-r--r--src/emscripten-optimizer/parser.cpp1
-rw-r--r--src/emscripten-optimizer/parser.h1
-rw-r--r--src/emscripten-optimizer/simple_ast.cpp102
-rw-r--r--src/emscripten-optimizer/simple_ast.h164
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) {