summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/asm2wasm.h415
-rw-r--r--src/emscripten-optimizer/optimizer-shared.cpp66
-rw-r--r--src/emscripten-optimizer/optimizer.h5
-rw-r--r--src/emscripten-optimizer/parser.cpp4
-rw-r--r--src/emscripten-optimizer/parser.h4
-rw-r--r--src/emscripten-optimizer/simple_ast.cpp153
-rw-r--r--src/emscripten-optimizer/simple_ast.h879
-rw-r--r--src/mixed_arena.h76
-rw-r--r--src/passes/MergeBlocks.cpp2
-rw-r--r--src/wasm-linker.cpp5
10 files changed, 521 insertions, 1088 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 77be6aad2..eaf4f8c18 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -299,8 +299,8 @@ private:
std::map<IString, std::unique_ptr<FunctionType>> 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<FunctionType>();
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[0] == 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[0] == 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;
}
@@ -456,31 +456,31 @@ private:
std::map<unsigned, Ref> tempNums;
Literal checkLiteral(Ref ast, bool rawIsInteger = true) {
- if (ast[0] == NUM) {
+ if (ast->isNumber()) {
if (rawIsInteger) {
- return Literal((int32_t)ast[1]->getInteger());
+ return Literal((int32_t)ast->getInteger());
} else {
- return Literal(ast[1]->getNumber());
+ return Literal(ast->getNumber());
}
- } else if (ast[0] == UNARY_PREFIX) {
- if (ast[1] == PLUS && ast[2][0] == NUM) {
- return Literal((double)ast[2][1]->getNumber());
+ } else if (ast->isArray(UNARY_PREFIX)) {
+ if (ast[1] == PLUS && ast[2]->isNumber()) {
+ return Literal((double)ast[2]->getNumber());
}
- if (ast[1] == MINUS && ast[2][0] == NUM) {
- double num = -ast[2][1]->getNumber();
+ if (ast[1] == MINUS && ast[2]->isNumber()) {
+ double num = -ast[2]->getNumber();
if (isSInteger32(num)) return Literal((int32_t)num);
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][0] == NUM) {
- return Literal((double)-ast[2][2][1]->getNumber());
+ 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][0] == NUM) {
- return Literal((double)-ast[2][2][1]->getNumber());
+ 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) {
- uint64_t low = ast[2][0][1]->getNumber();
- uint64_t high = ast[2][1][1]->getNumber();
+ } 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)));
}
return Literal();
@@ -572,15 +572,15 @@ 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);
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) {
@@ -709,34 +709,34 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
Ref pair = curr[1][j];
IString name = pair[0]->getIString();
Ref value = pair[1];
- if (value[0] == NUM) {
+ if (value->isNumber()) {
// global int
- assert(value[1]->getNumber() == 0);
+ assert(value->getNumber() == 0);
allocateGlobal(name, WasmType::i32);
} else if (value[0] == BINARY) {
// int import
- assert(value[1] == OR && value[3][0] == NUM && value[3][1]->getNumber() == 0);
+ assert(value[1] == OR && value[3]->isNumber() && value[3]->getNumber() == 0);
Ref import = value[2]; // env.what
addImport(name, import, WasmType::i32);
} else if (value[0] == UNARY_PREFIX) {
// double import or global
assert(value[1] == PLUS);
Ref import = value[2];
- if (import[0] == NUM) {
+ if (import->isNumber()) {
// global
- assert(import[1]->getNumber() == 0);
+ assert(import->getNumber() == 0);
allocateGlobal(name, WasmType::f64);
} else {
// import
addImport(name, import, WasmType::f64);
}
} else if (value[0] == CALL) {
- assert(value[1][0] == NAME && value[1][1] == Math_fround && value[2][0][0] == NUM && value[2][0][1]->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][0] == 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));
@@ -875,9 +875,9 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
}
} else {
// export a number. create a global and export it
- assert(pair[1][0] == NUM);
+ assert(pair[1]->isNumber());
assert(exported.count(key) == 0);
- auto value = pair[1][1]->getInteger();
+ auto value = pair[1]->getInteger();
auto global = new Global();
global->name = key;
global->type = i32;
@@ -1192,17 +1192,15 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
for (unsigned i = 0; i < params->size(); i++) {
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();
- AsmType asmType = detectType(curr[3], nullptr, false, Math_fround, wasmOnly);
+ auto* assign = curr->asAssignName();
+ IString name = assign->target();
+ AsmType asmType = detectType(assign->value(), nullptr, false, Math_fround, wasmOnly);
Builder::addParam(function, name, asmToWasmType(asmType));
functionVariables.insert(name);
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];
@@ -1231,63 +1229,108 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
std::function<Expression* (Ref)> process = [&](Ref ast) -> Expression* {
AstStackHelper astStackHelper(ast); // TODO: only create one when we need it?
- 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][0] == NAME) {
- IString name = ast[2][1]->getIString();
- if (functionVariables.has(name)) {
- auto ret = allocator.alloc<SetLocal>();
- ret->index = function->getLocalIndex(ast[2][1]->getIString());
- ret->value = process(ast[3]);
- ret->setTee(false);
- ret->finalize();
- return ret;
+ if (ast->isString()) {
+ IString name = ast->getIString();
+ if (functionVariables.has(name)) {
+ // var in scope
+ auto ret = allocator.alloc<GetLocal>();
+ ret->index = function->getLocalIndex(name);
+ ret->type = asmToWasmType(asmData.getType(name));
+ return ret;
+ }
+ if (name == DEBUGGER) {
+ CallImport *call = allocator.alloc<CallImport>();
+ 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);
}
- // global var
- assert(mappedGlobals.find(name) != mappedGlobals.end());
- auto* ret = builder.makeSetGlobal(name, process(ast[3]));
- // 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;
- 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(views.find(heap) != views.end());
- View& view = views[heap];
- auto ret = allocator.alloc<Store>();
- ret->bytes = view.bytes;
- ret->offset = 0;
- ret->align = view.bytes;
- ret->ptr = processUnshifted(target[2], view.bytes);
- ret->value = process(ast[3]);
- ret->valueType = asmToWasmType(view.type);
+ 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<Const>();
+ double num = ast->getNumber();
+ if (isSInteger32(num)) {
+ ret->value = Literal(int32_t(toSInteger32(num)));
+ } else if (isUInteger32(num)) {
+ ret->value = Literal(uint32_t(toUInteger32(num)));
+ } else {
+ ret->value = Literal(num);
+ }
+ ret->type = ret->value.type;
+ return ret;
+ }
+ if (ast->isAssignName()) {
+ auto* assign = ast->asAssignName();
+ IString name = assign->target();
+ if (functionVariables.has(name)) {
+ auto ret = allocator.alloc<SetLocal>();
+ ret->index = function->getLocalIndex(assign->target());
+ ret->value = process(assign->value());
+ ret->setTee(false);
ret->finalize();
- if (ret->valueType != ret->value->type) {
- // in asm.js we have some implicit coercions that we must do explicitly here
- if (ret->valueType == f32 && ret->value->type == f64) {
- auto conv = allocator.alloc<Unary>();
- conv->op = DemoteFloat64;
- conv->value = ret->value;
- conv->type = WasmType::f32;
- ret->value = conv;
- } else if (ret->valueType == f64 && ret->value->type == f32) {
- auto conv = allocator.alloc<Unary>();
- conv->op = PromoteFloat32;
- conv->value = ret->value;
- conv->type = WasmType::f64;
- ret->value = conv;
- } else {
- abort_on("bad sub[] types", ast);
- }
- }
return ret;
}
- abort_on("confusing assign", ast);
- } else if (what == BINARY) {
- if ((ast[1] == OR || ast[1] == TRSHIFT) && ast[3][0] == NUM && ast[3][1]->getNumber() == 0) {
+ // global var
+ 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)
+ auto parent = astStackHelper.getParent();
+ if (!parent || parent->isArray(BLOCK) || parent->isArray(IF)) return ret;
+ return builder.makeSequence(ret, builder.makeGetGlobal(name, ret->value->type));
+ }
+ if (ast->isAssign()) {
+ auto* assign = ast->asAssign();
+ assert(assign->target()->isArray(SUB));
+ Ref target = assign->target();
+ assert(target[1]->isString());
+ IString heap = target[1]->getIString();
+ assert(views.find(heap) != views.end());
+ View& view = views[heap];
+ auto ret = allocator.alloc<Store>();
+ ret->bytes = view.bytes;
+ ret->offset = 0;
+ ret->align = view.bytes;
+ ret->ptr = processUnshifted(target[2], view.bytes);
+ ret->value = process(assign->value());
+ ret->valueType = asmToWasmType(view.type);
+ ret->finalize();
+ if (ret->valueType != ret->value->type) {
+ // in asm.js we have some implicit coercions that we must do explicitly here
+ if (ret->valueType == f32 && ret->value->type == f64) {
+ auto conv = allocator.alloc<Unary>();
+ conv->op = DemoteFloat64;
+ conv->value = ret->value;
+ conv->type = WasmType::f32;
+ ret->value = conv;
+ } else if (ret->valueType == f64 && ret->value->type == f32) {
+ auto conv = allocator.alloc<Unary>();
+ conv->op = PromoteFloat32;
+ conv->value = ret->value;
+ conv->type = WasmType::f64;
+ ret->value = conv;
+ } else {
+ abort_on("bad sub[] types", ast);
+ }
+ }
+ return ret;
+ }
+ IString what = ast[0]->getIString();
+ 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);
return ret;
@@ -1344,52 +1387,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
return call;
}
return ret;
- } else if (what == NUM) {
- auto ret = allocator.alloc<Const>();
- double num = ast[1]->getNumber();
- if (isSInteger32(num)) {
- ret->value = Literal(int32_t(toSInteger32(num)));
- } else if (isUInteger32(num)) {
- ret->value = Literal(uint32_t(toUInteger32(num)));
- } else {
- ret->value = Literal(num);
- }
- ret->type = ret->value.type;
- return ret;
- } else if (what == NAME) {
- IString name = ast[1]->getIString();
- if (functionVariables.has(name)) {
- // var in scope
- auto ret = allocator.alloc<GetLocal>();
- ret->index = function->getLocalIndex(name);
- ret->type = asmToWasmType(asmData.getType(name));
- return ret;
- }
- if (name == DEBUGGER) {
- CallImport *call = allocator.alloc<CallImport>();
- 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<Load>();
@@ -1424,7 +1425,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
fixCallType(ret, f64);
return ret;
} else if (ast[1] == MINUS) {
- if (ast[2][0] == NUM || (ast[2][0] == UNARY_PREFIX && ast[2][1] == PLUS && ast[2][2][0] == NUM)) {
+ if (ast[2]->isNumber() || (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == PLUS && ast[2][2]->isNumber())) {
auto ret = allocator.alloc<Const>();
ret->value = getLiteral(ast);
ret->type = ret->value.type;
@@ -1454,7 +1455,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
return ret;
} else if (ast[1] == B_NOT) {
// ~, might be ~~ as a coercion or just a not
- if (ast[2][0] == UNARY_PREFIX && ast[2][1] == B_NOT) {
+ if (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == B_NOT) {
if (imprecise) {
auto ret = allocator.alloc<Unary>();
ret->value = process(ast[2][2]);
@@ -1509,8 +1510,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<Binary>();
@@ -1639,7 +1640,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
auto num = ast[2]->size();
switch (name.str[0]) {
case 'l': {
- auto align = num == 2 ? ast[2][1][1]->getInteger() : 0;
+ auto align = num == 2 ? ast[2][1]->getInteger() : 0;
if (name == LOAD1) return builder.makeLoad(1, true, 0, 1, process(ast[2][0]), i32);
if (name == LOAD2) return builder.makeLoad(2, true, 0, indexOr(align, 2), process(ast[2][0]), i32);
if (name == LOAD4) return builder.makeLoad(4, true, 0, indexOr(align, 4), process(ast[2][0]), i32);
@@ -1649,7 +1650,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
break;
}
case 's': {
- auto align = num == 3 ? ast[2][2][1]->getInteger() : 0;
+ auto align = num == 3 ? ast[2][2]->getInteger() : 0;
if (name == STORE1) return builder.makeStore(1, 0, 1, process(ast[2][0]), process(ast[2][1]), i32);
if (name == STORE2) return builder.makeStore(2, 0, indexOr(align, 2), process(ast[2][0]), process(ast[2][1]), i32);
if (name == STORE4) return builder.makeStore(4, 0, indexOr(align, 4), process(ast[2][0]), process(ast[2][1]), i32);
@@ -1788,7 +1789,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
// function pointers
auto ret = allocator.alloc<CallIndirect>();
Ref target = ast[1];
- assert(target[0] == SUB && target[1][0] == NAME && target[2][0] == BINARY && target[2][1] == AND && target[2][3][0] == NUM); // 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++) {
@@ -1798,7 +1799,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;
@@ -1845,7 +1846,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
ret->name = !!ast[1] ? nameMapper.sourceToUnique(getContinueLabelName(ast[1]->getIString())) : continueStack.back();
return ret;
} else if (what == WHILE) {
- bool forever = ast[1][0] == NUM && ast[1][1]->getInteger() == 1;
+ bool forever = ast[1]->isNumber() && ast[1]->getInteger() == 1;
auto ret = allocator.alloc<Loop>();
IString out, in;
if (!parentLabel.isNull()) {
@@ -1890,7 +1891,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
nameMapper.popLabelName(out);
return ret;
} else if (what == DO) {
- if (ast[1][0] == NUM && ast[1][1]->getNumber() == 0) {
+ if (ast[1]->isNumber() && ast[1]->getNumber() == 0) {
// one-time loop, unless there is a continue
IString stop;
if (!parentLabel.isNull()) {
@@ -2020,52 +2021,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][0] == ASSIGN && ast[1][2][0] == SUB && ast[1][2][1][0] == NAME && ast[1][2][2][0] == BINARY && ast[1][2][2][1] == RSHIFT &&
- ast[1][2][2][2][0] == NAME && ast[1][2][2][2][1] == tempDoublePtr && ast[1][2][2][3][0] == NUM && ast[1][2][2][3][1]->getNumber() == 2) {
- // (?[tempDoublePtr >> 2] = ?, ?) so far
- auto heap = ast[1][2][1][1]->getIString();
- if (views.find(heap) != views.end()) {
- AsmType writeType = views[heap].type;
- AsmType readType = ASM_NONE;
- Ref readValue;
- if (ast[2][0] == BINARY && ast[2][1] == OR && ast[2][3][0] == NUM && ast[2][3][1]->getNumber() == 0) {
- readType = ASM_INT;
- readValue = ast[2][2];
- } else if (ast[2][0] == UNARY_PREFIX && ast[2][1] == PLUS) {
- readType = ASM_DOUBLE;
- readValue = ast[2][2];
- } else if (ast[2][0] == CALL && ast[2][1][0] == NAME && ast[2][1][1] == Math_fround) {
- readType = ASM_FLOAT;
- readValue = ast[2][2][0];
- }
- if (readType != ASM_NONE) {
- if (readValue[0] == SUB && readValue[1][0] == NAME && readValue[2][0] == BINARY && readValue[2][1] == RSHIFT &&
- readValue[2][2][0] == NAME && readValue[2][2][1] == tempDoublePtr && readValue[2][3][0] == NUM && readValue[2][3][1]->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;
}
}
}
@@ -2242,12 +2247,12 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
processUnshifted = [&](Ref ptr, unsigned bytes) {
auto shifts = bytesToShift(bytes);
// HEAP?[addr >> ?], or HEAP8[x | 0]
- if ((ptr[0] == BINARY && ptr[1] == RSHIFT && ptr[3][0] == NUM && ptr[3][1]->getInteger() == shifts) ||
- (bytes == 1 && ptr[0] == BINARY && ptr[1] == OR && ptr[3][0] == NUM && ptr[3][1]->getInteger() == 0)) {
+ if ((ptr->isArray(BINARY) && ptr[1] == RSHIFT && ptr[3]->isNumber() && ptr[3]->getInteger() == shifts) ||
+ (bytes == 1 && ptr->isArray(BINARY) && ptr[1] == OR && ptr[3]->isNumber() && ptr[3]->getInteger() == 0)) {
return process(ptr[2]); // look through it
- } else if (ptr[0] == NUM) {
+ } else if (ptr->isNumber()) {
// constant, apply a shift (e.g. HEAP32[1] is address 4)
- unsigned addr = ptr[1]->getInteger();
+ unsigned addr = ptr->getInteger();
unsigned shifted = addr << shifts;
return (Expression*)builder.makeConst(Literal(int32_t(shifted)));
}
diff --git a/src/emscripten-optimizer/optimizer-shared.cpp b/src/emscripten-optimizer/optimizer-shared.cpp
index 57b7921fa..7ba6d4a22 100644
--- a/src/emscripten-optimizer/optimizer-shared.cpp
+++ b/src/emscripten-optimizer/optimizer-shared.cpp
@@ -53,28 +53,26 @@ HeapInfo parseHeap(const char *name) {
}
AsmType detectType(Ref node, AsmData *asmData, bool inVarDef, IString minifiedFround, bool allowI64) {
- switch (node[0]->getCString()[0]) {
- case 'n': {
- if (node[0] == NUM) {
- if (!wasm::isInteger(node[1]->getNumber())) return ASM_DOUBLE;
- return ASM_INT;
- } else if (node[0] == NAME) {
- if (asmData) {
- AsmType ret = asmData->getType(node[1]->getCString());
- if (ret != ASM_NONE) return ret;
- }
- if (!inVarDef) {
- if (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;
+ 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 'u': {
if (node[0] == UNARY_PREFIX) {
switch (node[1]->getCString()[0]) {
@@ -88,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;
@@ -121,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?
@@ -141,6 +139,16 @@ 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;
+ if (value > uint32_t(-1) || fmod(value, 1) != 0) return ASM_NONSIGNED;
+ if (wasm::isSInteger32(value)) return ASM_FLEXIBLE;
+ return ASM_UNSIGNED;
+ }
IString type = node[0]->getIString();
if (type == BINARY) {
IString op = node[1]->getIString();
@@ -162,18 +170,10 @@ AsmSign detectSign(Ref node, IString minifiedFround) {
case '~': return ASM_SIGNED;
default: abort_on(node);
}
- } else if (type == NUM) {
- double value = node[1]->getNumber();
- if (value < 0) return ASM_SIGNED;
- if (value > uint32_t(-1) || fmod(value, 1) != 0) return ASM_NONSIGNED;
- if (wasm::isSInteger32(value)) return ASM_FLEXIBLE;
- return ASM_UNSIGNED;
- } else if (type == NAME) {
- return ASM_FLEXIBLE;
} else if (type == CONDITIONAL) {
return detectSign(node[2], minifiedFround);
} else if (type == CALL) {
- if (node[1][0] == NAME && (node[1][1] == MATH_FROUND || node[1][1] == minifiedFround)) return ASM_NONSIGNED;
+ 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/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 064c0efcf..227ce66a5 100644
--- a/src/emscripten-optimizer/parser.cpp
+++ b/src/emscripten-optimizer/parser.cpp
@@ -23,9 +23,6 @@ namespace cashew {
IString TOPLEVEL("toplevel"),
DEFUN("defun"),
BLOCK("block"),
- STAT("stat"),
- ASSIGN("assign"),
- NAME("name"),
VAR("var"),
CONST("const"),
CONDITIONAL("conditional"),
@@ -39,7 +36,6 @@ IString TOPLEVEL("toplevel"),
SEQ("seq"),
SUB("sub"),
CALL("call"),
- NUM("num"),
LABEL("label"),
BREAK("break"),
CONTINUE("continue"),
diff --git a/src/emscripten-optimizer/parser.h b/src/emscripten-optimizer/parser.h
index 060b71272..5c9aa2c8c 100644
--- a/src/emscripten-optimizer/parser.h
+++ b/src/emscripten-optimizer/parser.h
@@ -38,9 +38,6 @@ namespace cashew {
extern IString TOPLEVEL,
DEFUN,
BLOCK,
- STAT,
- ASSIGN,
- NAME,
VAR,
CONST,
CONDITIONAL,
@@ -54,7 +51,6 @@ extern IString TOPLEVEL,
SEQ,
SUB,
CALL,
- NUM,
LABEL,
BREAK,
CONTINUE,
diff --git a/src/emscripten-optimizer/simple_ast.cpp b/src/emscripten-optimizer/simple_ast.cpp
index dddaeab02..75685f80e 100644
--- a/src/emscripten-optimizer/simple_ast.cpp
+++ b/src/emscripten-optimizer/simple_ast.cpp
@@ -54,31 +54,124 @@ bool Ref::operator!() {
// Arena
-Arena arena;
+GlobalMixedArena arena;
-Arena::~Arena() {
- for (auto* chunk : chunks) {
- delete[] chunk;
- }
- for (auto* chunk : arr_chunks) {
- delete[] chunk;
- }
+// Value
+
+Value& Value::setAssign(Ref target, Ref value) {
+ asAssign()->target() = target;
+ asAssign()->value() = value;
+ return *this;
}
-Ref Arena::alloc() {
- if (chunks.size() == 0 || index == CHUNK_SIZE) {
- chunks.push_back(new Value[CHUNK_SIZE]);
- index = 0;
- }
- return &chunks.back()[index++];
+Value& Value::setAssignName(IString target, Ref value) {
+ asAssignName()->target() = target;
+ asAssignName()->value() = value;
+ return *this;
+}
+
+Assign* Value::asAssign() {
+ assert(isAssign());
+ return static_cast<Assign*>(this);
+}
+
+AssignName* Value::asAssignName() {
+ assert(isAssignName());
+ return static_cast<AssignName*>(this);
}
-ArrayStorage* Arena::allocArray() {
- if (arr_chunks.size() == 0 || arr_index == CHUNK_SIZE) {
- arr_chunks.push_back(new ArrayStorage[CHUNK_SIZE]);
- arr_index = 0;
+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;
+ }
+ case AssignName_: {
+ os << "[\"" << asAssignName()->target().str << "\"";
+ os << ", ";
+ asAssignName()->value()->stringify(os, pretty);
+ os << "]";
+ break;
+ }
}
- return &arr_chunks.back()[arr_index++];
}
// dump
@@ -161,7 +254,7 @@ void traversePre(Ref node, std::function<void (Ref)> visit) {
int index = 0;
ArrayStorage* arr = &node->getArray();
int arrsize = (int)arr->size();
- Ref* arrdata = arr->data();
+ Ref* arrdata = &(*arr)[0];
stack.push_back(TraverseInfo(node, arr));
while (1) {
if (index < arrsize) {
@@ -173,7 +266,7 @@ void traversePre(Ref node, std::function<void (Ref)> visit) {
visit(sub);
arr = &sub->getArray();
arrsize = (int)arr->size();
- arrdata = arr->data();
+ arrdata = &(*arr)[0];
stack.push_back(TraverseInfo(sub, arr));
}
} else {
@@ -183,7 +276,7 @@ void traversePre(Ref node, std::function<void (Ref)> visit) {
index = back.index;
arr = back.arr;
arrsize = (int)arr->size();
- arrdata = arr->data();
+ arrdata = &(*arr)[0];
}
}
}
@@ -196,7 +289,7 @@ void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function
int index = 0;
ArrayStorage* arr = &node->getArray();
int arrsize = (int)arr->size();
- Ref* arrdata = arr->data();
+ Ref* arrdata = &(*arr)[0];
stack.push_back(TraverseInfo(node, arr));
while (1) {
if (index < arrsize) {
@@ -208,7 +301,7 @@ void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function
visitPre(sub);
arr = &sub->getArray();
arrsize = (int)arr->size();
- arrdata = arr->data();
+ arrdata = &(*arr)[0];
stack.push_back(TraverseInfo(sub, arr));
}
} else {
@@ -219,7 +312,7 @@ void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function
index = back.index;
arr = back.arr;
arrsize = (int)arr->size();
- arrdata = arr->data();
+ arrdata = &(*arr)[0];
}
}
}
@@ -232,7 +325,7 @@ void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, st
int index = 0;
ArrayStorage* arr = &node->getArray();
int arrsize = (int)arr->size();
- Ref* arrdata = arr->data();
+ Ref* arrdata = &(*arr)[0];
stack.push_back(TraverseInfo(node, arr));
while (1) {
if (index < arrsize) {
@@ -244,7 +337,7 @@ void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, st
index = 0;
arr = &sub->getArray();
arrsize = (int)arr->size();
- arrdata = arr->data();
+ arrdata = &(*arr)[0];
stack.push_back(TraverseInfo(sub, arr));
}
}
@@ -256,7 +349,7 @@ void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, st
index = back.index;
arr = back.arr;
arrsize = (int)arr->size();
- arrdata = arr->data();
+ arrdata = &(*arr)[0];
}
}
}
@@ -275,8 +368,4 @@ void traverseFunctions(Ref ast, std::function<void (Ref)> visit) {
}
}
-// ValueBuilder
-
-IStringSet ValueBuilder::statable("assign call binary unary-prefix name num conditional dot new sub seq string object array");
-
} // namespace cashew
diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h
index 20de952c1..efbd7b6f6 100644
--- a/src/emscripten-optimizer/simple_ast.h
+++ b/src/emscripten-optimizer/simple_ast.h
@@ -36,6 +36,7 @@
#include "parser.h"
#include "snprintf.h"
#include "support/safe_integer.h"
+#include "mixed_arena.h"
#define err(str) fprintf(stderr, str "\n");
#define errv(str, ...) fprintf(stderr, str "\n", __VA_ARGS__);
@@ -43,8 +44,8 @@
namespace cashew {
-struct Ref;
struct Value;
+struct Ref;
void dump(const char *str, Ref node, bool pretty=false);
@@ -73,24 +74,30 @@ struct Ref {
// Arena allocation, free it all on process exit
-typedef std::vector<Ref> ArrayStorage;
-
-struct Arena {
- #define CHUNK_SIZE 1000
- std::vector<Value*> chunks;
- int index; // in last chunk
-
- std::vector<ArrayStorage*> arr_chunks;
- int arr_index;
+// A mixed arena for global allocation only, so members do not
+// receive an allocator, they all use the global one anyhow
+class GlobalMixedArena : public MixedArena {
+public:
+ template<class T>
+ T* alloc() {
+ auto* ret = static_cast<T*>(allocSpace(sizeof(T)));
+ new (ret) T();
+ return ret;
+ }
+};
- Arena() : index(0), arr_index(0) {}
- ~Arena();
+extern GlobalMixedArena arena;
- Ref alloc();
- ArrayStorage* allocArray();
+class ArrayStorage : public ArenaVectorBase<ArrayStorage, Ref> {
+public:
+ void allocate(size_t size) {
+ allocatedElements = size;
+ data = static_cast<Ref*>(arena.allocSpace(sizeof(Ref) * allocatedElements));
+ }
};
-extern Arena arena;
+struct Assign;
+struct AssignName;
// Main value type
struct Value {
@@ -100,7 +107,9 @@ struct Value {
Array = 2,
Null = 3,
Bool = 4,
- Object = 5
+ Object = 5,
+ Assign_ = 6, // ref = target
+ AssignName_ = 7
};
Type type;
@@ -118,6 +127,7 @@ struct Value {
ArrayStorage *arr;
bool boo;
ObjectStorage *obj;
+ Ref ref;
};
// constructors all copy their input
@@ -139,7 +149,7 @@ struct Value {
}
void free() {
- if (type == Array) { arr->clear(); arr->shrink_to_fit(); }
+ if (type == Array) { arr->clear(); }
else if (type == Object) delete obj;
type = Null;
num = 0;
@@ -166,14 +176,14 @@ struct Value {
Value& setArray(ArrayStorage &a) {
free();
type = Array;
- arr = arena.allocArray();
+ arr = arena.alloc<ArrayStorage>();
*arr = a;
return *this;
}
Value& setArray(size_t size_hint=0) {
free();
type = Array;
- arr = arena.allocArray();
+ arr = arena.alloc<ArrayStorage>();
arr->reserve(size_hint);
return *this;
}
@@ -194,16 +204,28 @@ struct Value {
obj = new ObjectStorage();
return *this;
}
+ Value& setAssign(Ref target, Ref value);
+ Value& setAssignName(IString 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 isAssignName() { return type == AssignName_; }
bool isBool(bool b) { return type == Bool && b == boo; } // avoid overloading == as it might overload over int
+ // convenience function to check if something is an array and
+ // also has a certain string as the first element. This is a
+ // very common operation as the first element defines the node
+ // type for most ast nodes
+ bool isArray(IString name) {
+ return isArray() && (*this)[0] == name;
+ }
+
const char* getCString() {
assert(isString());
return str.str;
@@ -225,6 +247,9 @@ struct Value {
return boo;
}
+ Assign* asAssign();
+ AssignName* asAssignName();
+
int32_t getInteger() { // convenience function to get a known integer
assert(fmod(getNumber(), 1) == 0);
int32_t ret = getNumber();
@@ -250,7 +275,7 @@ struct Value {
case Bool:
setBool(other.boo);
break;
- case Object:
+ default:
abort(); // TODO
}
return *this;
@@ -271,31 +296,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++; }
@@ -314,7 +320,7 @@ struct Value {
skip();
setArray();
while (*curr != ']') {
- Ref temp = arena.alloc();
+ Ref temp = arena.alloc<Value>();
arr->push_back(temp);
curr = temp->parse(curr);
skip();
@@ -356,7 +362,7 @@ struct Value {
assert(*curr == ':');
curr++;
skip();
- Ref value = arena.alloc();
+ Ref value = arena.alloc<Value>();
curr = value->parse(curr);
(*obj)[key] = value;
skip();
@@ -375,78 +381,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
@@ -465,14 +400,14 @@ struct Value {
if (old != size) arr->resize(size);
if (old < size) {
for (auto i = old; i < size; i++) {
- (*arr)[i] = arena.alloc();
+ (*arr)[i] = arena.alloc<Value>();
}
}
}
Ref& operator[](unsigned x) {
assert(isArray());
- return arr->at(x);
+ return (*arr)[x];
}
Value& push_back(Ref r) {
@@ -493,18 +428,6 @@ struct Value {
return arr->back();
}
- void splice(int x, int num) {
- assert(isArray());
- arr->erase(arr->begin() + x, arr->begin() + x + num);
- }
-
- void insert(int x, int num) {
- arr->insert(arr->begin() + x, num, Ref());
- }
- void insert(int x, Ref node) {
- arr->insert(arr->begin() + x, 1, node);
- }
-
int indexOf(Ref other) {
assert(isArray());
for (size_t i = 0; i < arr->size(); i++) {
@@ -515,7 +438,7 @@ struct Value {
Ref map(std::function<Ref (Ref node)> func) {
assert(isArray());
- Ref ret = arena.alloc();
+ Ref ret = arena.alloc<Value>();
ret->setArray();
for (size_t i = 0; i < arr->size(); i++) {
ret->push_back(func((*arr)[i]));
@@ -525,7 +448,7 @@ struct Value {
Ref filter(std::function<bool (Ref node)> func) {
assert(isArray());
- Ref ret = arena.alloc();
+ Ref ret = arena.alloc<Value>();
ret->setArray();
for (size_t i = 0; i < arr->size(); i++) {
Ref curr = (*arr)[i];
@@ -559,281 +482,61 @@ struct Value {
}
};
-// AST traversals
-
-// Traverse, calling visit before the children
-void traversePre(Ref node, std::function<void (Ref)> visit);
-
-// Traverse, calling visitPre before the children and visitPost after
-void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost);
-
-// Traverse, calling visitPre before the children and visitPost after. If pre returns false, do not traverse children
-void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, std::function<void (Ref)> visitPost);
-
-// Traverses all the top-level functions in the document
-void traverseFunctions(Ref ast, std::function<void (Ref)> visit);
-
-// JS printer
-
-struct JSPrinter {
- bool pretty, finalize;
-
- char *buffer;
- size_t size, used;
-
- int indent;
- bool possibleSpace; // add a space to separate identifiers
-
- Ref ast;
+struct Assign : public Value {
+ Ref value_;
- 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;
+ Assign(Ref targetInit, Ref valueInit) {
+ type = Assign_;
+ target() = targetInit;
+ value() = valueInit;
}
- // Utils
+ Assign() : Assign(nullptr, nullptr) {}
- 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;
- }
- }
+ Ref& target() {
+ return ref;
}
-
- 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;
+ Ref& value() {
+ return value_;
}
+};
- void emit(const char *s) {
- maybeSpace(*s);
- int len = strlen(s);
- ensure(len+1);
- strncpy(buffer + used, s, len+1);
- used += len;
- }
+struct AssignName : public Value {
+ IString target_;
- void newline() {
- if (!pretty) return;
- emit('\n');
- for (int i = 0; i < indent; i++) emit(' ');
+ AssignName(IString targetInit, Ref valueInit) {
+ type = AssignName_;
+ target() = targetInit;
+ value() = valueInit;
}
- void space() {
- if (pretty) emit(' ');
- }
+ AssignName() : AssignName(IString(), nullptr) {}
- void safeSpace() {
- if (pretty) emit(' ');
- else possibleSpace = true;
+ IString& target() {
+ return target_;
}
-
- void maybeSpace(char s) {
- if (possibleSpace) {
- possibleSpace = false;
- if (isIdentPart(s)) emit(' ');
- }
- }
-
- void print(Ref node) {
- ensure();
- 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);
- 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 == NAME) printName(node);
- else if (type == NUM) printNum(node);
- else 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]);
- }
- }
+ Ref& value() {
+ return ref;
}
+};
- void printToplevel(Ref node) {
- if (node[1]->size() > 0) {
- printStats(node[1]);
- }
- }
+// AST traversals
- 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();
- }
+// Traverse, calling visit before the children
+void traversePre(Ref node, std::function<void (Ref)> visit);
- bool isNothing(Ref node) {
- return (node[0] == TOPLEVEL && node[1]->size() == 0) || (node[0] == STAT && isNothing(node[1]));
- }
+// Traverse, calling visitPre before the children and visitPost after
+void traversePrePost(Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost);
- void printStat(Ref node) {
- if (!isNothing(node[1])) {
- print(node[1]);
- if (buffer[used-1] != ';') emit(';');
- }
- }
+// Traverse, calling visitPre before the children and visitPost after. If pre returns false, do not traverse children
+void traversePrePostConditional(Ref node, std::function<bool (Ref)> visitPre, std::function<void (Ref)> visitPost);
- void printAssign(Ref node) {
- printChild(node[2], node, -1);
- space();
- emit('=');
- space();
- printChild(node[3], node, 1);
- }
+// Traverses all the top-level functions in the document
+void traverseFunctions(Ref ast, std::function<void (Ref)> visit);
- void printName(Ref node) {
- emit(node[1]->getCString());
- }
+// JS printing support
+struct JSPrinter {
static char* numToString(double d, bool finalize=true) {
bool neg = d < 0;
if (neg) d = -d;
@@ -955,379 +658,22 @@ struct JSPrinter {
}
return ret;
}
-
- void printNum(Ref node) {
- emit(numToString(node[1]->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) {
- 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 == ASSIGN) {
- return OperatorClass::getPrecedence(OperatorClass::Binary, SET);
- } 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][0] == NUM ||
- (node[2][0] == UNARY_PREFIX && node[2][1] == MINUS && node[2][2][0] == NUM))) {
- // 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);
+ return &arena.alloc<Value>()->setString(s);
}
static Ref makeNull() {
- return &arena.alloc()->setNull();
+ return &arena.alloc<Value>()->setNull();
}
public:
static Ref makeRawArray(int size_hint=0) {
- return &arena.alloc()->setArray(size_hint);
+ return &arena.alloc<Value>()->setArray(size_hint);
}
static Ref makeToplevel() {
@@ -1346,8 +692,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) {
@@ -1449,17 +794,11 @@ public:
}
static Ref makeStatement(Ref contents) {
- if (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) {
- return &makeRawArray(2)->push_back(makeRawString(NUM))
- .push_back(&arena.alloc()->setNumber(num));
+ return &arena.alloc<Value>()->setNumber(num);
}
static Ref makeInt(uint32_t num) {
return makeDouble(double(num));
@@ -1476,10 +815,11 @@ 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);
+ if (left->isString()) {
+ return &arena.alloc<AssignName>()->setAssignName(left->getIString(), right);
+ } else {
+ return &arena.alloc<Assign>()->setAssign(left, right);
+ }
} else if (op == COMMA) {
return &makeRawArray(3)->push_back(makeRawString(SEQ))
.push_back(left)
@@ -1626,8 +966,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) {
@@ -1656,19 +996,6 @@ public:
.push_back(value));
}
- static Ref makeAssign(Ref target, Ref value) {
- return &makeRawArray(3)->push_back(makeRawString(ASSIGN))
- .push_back(&arena.alloc()->setBool(true))
- .push_back(target)
- .push_back(value);
- }
- static Ref makeAssign(IString target, Ref value) {
- return &makeRawArray(3)->push_back(makeRawString(ASSIGN))
- .push_back(&arena.alloc()->setBool(true))
- .push_back(makeName(target))
- .push_back(value);
- }
-
static Ref makeSub(Ref obj, Ref index) {
return &makeRawArray(2)->push_back(makeRawString(SUB))
.push_back(obj)
diff --git a/src/mixed_arena.h b/src/mixed_arena.h
index 28b2dd462..52e47fbde 100644
--- a/src/mixed_arena.h
+++ b/src/mixed_arena.h
@@ -144,39 +144,24 @@ struct MixedArena {
//
// A vector that allocates in an arena.
//
-// TODO: consider not saving the allocator, but requiring it be
-// passed in when needed, would make this (and thus Blocks etc.
-// smaller)
-//
// TODO: specialize on the initial size of the array
-template <typename T>
-class ArenaVector {
- MixedArena& allocator;
+template <typename SubType, typename T>
+class ArenaVectorBase {
+protected:
T* data = nullptr;
size_t usedElements = 0,
allocatedElements = 0;
- void allocate(size_t size) {
- allocatedElements = size;
- data = static_cast<T*>(allocator.allocSpace(sizeof(T) * allocatedElements));
- }
-
void reallocate(size_t size) {
T* old = data;
- allocate(size);
+ static_cast<SubType*>(this)->allocate(size);
for (size_t i = 0; i < usedElements; i++) {
data[i] = old[i];
}
}
public:
- ArenaVector(MixedArena& allocator) : allocator(allocator) {}
-
- ArenaVector(ArenaVector<T>&& other) : allocator(other.allocator) {
- *this = other;
- }
-
T& operator[](size_t index) const {
assert(index < usedElements);
return data[index];
@@ -216,11 +201,21 @@ public:
usedElements++;
}
+ void clear() {
+ usedElements = 0;
+ }
+
+ void reserve(size_t size) {
+ if (size > allocatedElements) {
+ reallocate(size);
+ }
+ }
+
template<typename ListType>
void set(const ListType& list) {
size_t size = list.size();
if (allocatedElements < size) {
- allocate(size);
+ static_cast<SubType*>(this)->allocate(size);
}
for (size_t i = 0; i < size; i++) {
data[i] = list[i];
@@ -228,14 +223,15 @@ public:
usedElements = size;
}
- void operator=(ArenaVector<T>& other) {
+ void operator=(SubType& other) {
set(other);
}
- void operator=(ArenaVector<T>&& other) {
+ void swap(SubType& other) {
data = other.data;
usedElements = other.usedElements;
allocatedElements = other.allocatedElements;
+
other.data = nullptr;
other.usedElements = other.allocatedElements = 0;
}
@@ -243,10 +239,10 @@ public:
// iteration
struct Iterator {
- const ArenaVector<T>* parent;
+ const SubType* parent;
size_t index;
- Iterator(const ArenaVector<T>* parent, size_t index) : parent(parent), index(index) {}
+ Iterator(const SubType* parent, size_t index) : parent(parent), index(index) {}
bool operator!=(const Iterator& other) const {
return index != other.index || parent != other.parent;
@@ -262,10 +258,38 @@ public:
};
Iterator begin() const {
- return Iterator(this, 0);
+ return Iterator(static_cast<const SubType*>(this), 0);
}
Iterator end() const {
- return Iterator(this, usedElements);
+ return Iterator(static_cast<const SubType*>(this), usedElements);
+ }
+
+ void allocate(size_t size) {
+ abort(); // must be implemented in children
+ }
+};
+
+// A vector that has an allocator for arena allocation
+//
+// TODO: consider not saving the allocator, but requiring it be
+// passed in when needed, would make this (and thus Blocks etc.
+// smaller)
+
+template <typename T>
+class ArenaVector : public ArenaVectorBase<ArenaVector<T>, T> {
+private:
+ MixedArena& allocator;
+
+public:
+ ArenaVector(MixedArena& allocator) : allocator(allocator) {}
+
+ ArenaVector(ArenaVector<T>&& other) : allocator(other.allocator) {
+ *this = other;
+ }
+
+ void allocate(size_t size) {
+ this->allocatedElements = size;
+ this->data = static_cast<T*>(allocator.allocSpace(sizeof(T) * this->allocatedElements));
}
};
diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp
index 467ffb4ed..383837beb 100644
--- a/src/passes/MergeBlocks.cpp
+++ b/src/passes/MergeBlocks.cpp
@@ -190,7 +190,7 @@ static void optimizeBlock(Block* curr, Module* module) {
for (size_t j = i + 1; j < curr->list.size(); j++) {
merged.push_back(curr->list[j]);
}
- curr->list = merged;
+ curr->list.swap(merged);
more = true;
changed = true;
break;
diff --git a/src/wasm-linker.cpp b/src/wasm-linker.cpp
index 757fc88ca..c2fc00562 100644
--- a/src/wasm-linker.cpp
+++ b/src/wasm-linker.cpp
@@ -82,11 +82,12 @@ void Linker::layout() {
// behavior.
for (auto* call : f.second) {
auto type = call->type;
- auto operands = std::move(call->operands);
+ ExpressionList operands(out.wasm.allocator);
+ operands.swap(call->operands);
auto target = call->target;
CallImport* newCall = ExpressionManipulator::convert<Call, CallImport>(call, out.wasm.allocator);
newCall->type = type;
- newCall->operands = std::move(operands);
+ newCall->operands.swap(operands);
newCall->target = target;
}
}